diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index c69b2fce78..fb315ef04f 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -917,6 +917,15 @@ paths, where a space would otherwise separate filenames. See below.) `$:` :: a colon. (This is only necessary in `build` lines, where a colon would otherwise terminate the list of outputs.) +`$^` :: a newline _(available since Ninja 1.14)_. + +Inserts '\n' ('\r\n' on Windows) into the resulting string. This is useful to +write build commands that require several shell lines as a single `command` +value in the build plan. + +Requires `ninja_required_version` to be specified and greater or equal to 1.14 +in the build file. + `$$`:: a literal `$`. A `build` or `default` statement is first parsed as a space-separated diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 73ba69c3fb..f1d4072ddf 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -69,44 +69,42 @@ bool DepfileParser::Parse(string* content, string* err) { { 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, 128, 128, 0, 0, 128, 128, 128, - 128, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 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, 128, 128, 0, 128, 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, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, + static const unsigned char yybm[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 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, 0, 128, 128, 128, + 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 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, 128, 128, 0, 128, 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, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 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; - if (yybm[0+yych] & 128) { - goto yy5; - } + if (yybm[0+yych] & 128) goto yy5; if (yych <= '\r') { if (yych <= '\t') { if (yych >= 0x01) goto yy1; @@ -150,9 +148,7 @@ bool DepfileParser::Parse(string* content, string* err) { goto yy2; yy5: yych = *++in; - if (yybm[0+yych] & 128) { - goto yy5; - } + if (yybm[0+yych] & 128) goto yy5; yy6: { // Got a span of plain text. diff --git a/src/lexer.cc b/src/lexer.cc index 8edcf7b4f4..109736dbf3 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -22,6 +22,10 @@ using namespace std; +// $^ supported starting this version +const int minNewlineEscapeVersionMajor = 1; +const int minNewlineEscapeVersionMinor = 14; + bool Lexer::Error(const string& message, string* err) { // Compute line/column. int line = 1; @@ -128,44 +132,42 @@ Lexer::Token Lexer::ReadToken() { { unsigned char yych; unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 160, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 192, 192, 128, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 128, 128, 128, 128, 128, 128, - 128, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 128, 128, 128, 128, 192, - 128, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, + static const unsigned char yybm[256] = { + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 160, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 192, 192, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 128, 128, 128, 128, 192, + 128, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 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 (yybm[0+yych] & 32) { - goto yy6; - } + if (yybm[0+yych] & 32) goto yy6; if (yych <= '^') { if (yych <= ',') { if (yych <= '\f') { @@ -237,9 +239,7 @@ Lexer::Token Lexer::ReadToken() { yy6: yyaccept = 0; yych = *(q = ++p); - if (yybm[0+yych] & 32) { - goto yy6; - } + if (yybm[0+yych] & 32) goto yy6; if (yych <= '\f') { if (yych == '\n') goto yy4; } else { @@ -256,9 +256,7 @@ Lexer::Token Lexer::ReadToken() { yy9: yych = *++p; yy10: - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = IDENT; break; } yy11: ++p; @@ -303,17 +301,12 @@ Lexer::Token Lexer::ReadToken() { if (yych == '\n') goto yy20; yy22: p = q; - if (yyaccept == 0) { - goto yy7; - } else { - goto yy3; - } + if (yyaccept == 0) goto yy7; + else goto yy3; yy23: yych = *++p; yy24: - if (yybm[0+yych] & 128) { - goto yy23; - } + if (yybm[0+yych] & 128) goto yy23; if (yych <= 0x00) goto yy22; ++p; { continue; } @@ -385,15 +378,11 @@ Lexer::Token Lexer::ReadToken() { goto yy10; yy42: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = POOL; break; } yy43: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = RULE; break; } yy44: yych = *++p; @@ -401,9 +390,7 @@ Lexer::Token Lexer::ReadToken() { goto yy10; yy45: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = BUILD; break; } yy46: yych = *++p; @@ -431,23 +418,17 @@ Lexer::Token Lexer::ReadToken() { goto yy10; yy52: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = DEFAULT; break; } yy53: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = INCLUDE; break; } yy54: yych = *++p; if (yych != 'a') goto yy10; yych = *++p; - if (yybm[0+yych] & 64) { - goto yy9; - } + if (yybm[0+yych] & 64) goto yy9; { token = SUBNINJA; break; } } @@ -476,44 +457,42 @@ void Lexer::EatWhitespace() { { 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, + static const unsigned char yybm[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 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 (yybm[0+yych] & 128) { - goto yy59; - } + if (yybm[0+yych] & 128) goto yy59; if (yych <= 0x00) goto yy56; if (yych == '$') goto yy60; goto yy57; @@ -526,9 +505,7 @@ void Lexer::EatWhitespace() { { break; } yy59: yych = *++p; - if (yybm[0+yych] & 128) { - goto yy59; - } + if (yybm[0+yych] & 128) goto yy59; { continue; } yy60: yych = *(q = ++p); @@ -559,44 +536,42 @@ bool Lexer::ReadIdent(string* out) { { 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, + static const unsigned char yybm[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 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 (yybm[0+yych] & 128) { - goto yy65; - } + if (yybm[0+yych] & 128) goto yy65; ++p; { last_token_ = start; @@ -604,9 +579,7 @@ bool Lexer::ReadIdent(string* out) { } yy65: yych = *++p; - if (yybm[0+yych] & 128) { - goto yy65; - } + if (yybm[0+yych] & 128) goto yy65; { out->assign(start, p - start); break; @@ -629,44 +602,42 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { { unsigned char yych; - static const unsigned char yybm[] = { - 0, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 0, 16, 16, 0, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 32, 16, 16, 16, 0, 16, 16, 16, - 16, 16, 16, 16, 16, 208, 144, 16, - 208, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 0, 16, 16, 16, 16, 16, - 16, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 208, 16, 16, 16, 16, 208, - 16, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 208, 208, 208, 208, 208, 208, - 208, 208, 208, 16, 0, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, + static const unsigned char yybm[256] = { + 0, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 0, 16, 16, 0, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 32, 16, 16, 16, 0, 16, 16, 16, + 16, 16, 16, 16, 16, 208, 144, 16, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 0, 16, 16, 16, 16, 16, + 16, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 16, 16, 16, 16, 208, + 16, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 16, 0, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16 }; yych = *p; - if (yybm[0+yych] & 16) { - goto yy68; - } + if (yybm[0+yych] & 16) goto yy68; if (yych <= '\r') { if (yych <= 0x00) goto yy67; if (yych <= '\n') goto yy69; @@ -684,9 +655,7 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { } yy68: yych = *++p; - if (yybm[0+yych] & 16) { - goto yy68; - } + if (yybm[0+yych] & 16) goto yy68; { eval->AddText(StringPiece(start, p - start)); continue; @@ -713,26 +682,26 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { } yy71: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy79; - } - if (yych <= ' ') { + if (yybm[0+yych] & 64) goto yy79; + if (yych <= '#') { if (yych <= '\f') { if (yych == '\n') goto yy75; goto yy73; } else { if (yych <= '\r') goto yy76; - if (yych <= 0x1F) goto yy73; - goto yy77; + if (yych == ' ') goto yy77; + goto yy73; } } else { - if (yych <= '/') { - if (yych == '$') goto yy78; + if (yych <= ']') { + if (yych <= '$') goto yy78; + if (yych <= '/') goto yy73; + if (yych <= ':') goto yy80; goto yy73; } else { - if (yych <= ':') goto yy80; + if (yych <= '^') goto yy81; if (yych <= '`') goto yy73; - if (yych <= '{') goto yy81; + if (yych <= '{') goto yy82; goto yy73; } } @@ -752,15 +721,13 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { } yy75: yych = *++p; - if (yybm[0+yych] & 32) { - goto yy75; - } + if (yybm[0+yych] & 32) goto yy75; { continue; } yy76: yych = *++p; - if (yych == '\n') goto yy82; + if (yych == '\n') goto yy83; goto yy74; yy77: ++p; @@ -776,9 +743,7 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { } yy79: yych = *++p; - if (yybm[0+yych] & 64) { - goto yy79; - } + if (yybm[0+yych] & 64) goto yy79; { eval->AddSpecial(StringPiece(start + 1, p - start - 1)); continue; @@ -790,26 +755,42 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { continue; } yy81: + ++p; + { + if (!newline_version_checked_) + { + if ((manifest_version_major < minNewlineEscapeVersionMajor) || + (manifest_version_major = minNewlineEscapeVersionMajor && + manifest_version_minor < minNewlineEscapeVersionMinor)) + { + return Error("using $^ escape requires specifying 'ninja_required_version' with version greater or equal 1.14", err); + } + newline_version_checked_ = true; + } +#ifdef _WIN32 + eval->AddText(StringPiece("\r\n", 2)); +#else + eval->AddText(StringPiece("\n", 1)); +#endif + continue; + } +yy82: yych = *(q = ++p); - if (yybm[0+yych] & 128) { - goto yy83; - } + if (yybm[0+yych] & 128) goto yy84; goto yy74; -yy82: +yy83: yych = *++p; - if (yych == ' ') goto yy82; + if (yych == ' ') goto yy83; { continue; } -yy83: +yy84: yych = *++p; - if (yybm[0+yych] & 128) { - goto yy83; - } - if (yych == '}') goto yy84; + if (yybm[0+yych] & 128) goto yy84; + if (yych == '}') goto yy85; p = q; goto yy74; -yy84: +yy85: ++p; { eval->AddSpecial(StringPiece(start + 2, p - start - 3)); diff --git a/src/lexer.h b/src/lexer.h index 683fd6c6ab..21486303dc 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -90,6 +90,10 @@ struct Lexer { /// Construct an error message with context. bool Error(const std::string& message, std::string* err); + /// Parsed 'ninja_required_version' from the manifest. + int manifest_version_major = 0; + int manifest_version_minor = 0; + private: /// Skip past whitespace (called after each read token/ident/etc.). void EatWhitespace(); @@ -101,6 +105,9 @@ struct Lexer { StringPiece input_; const char* ofs_; const char* last_token_; + + /// Holds true if ninja_required_version checked for $^ (newline) escape. + bool newline_version_checked_ = false; }; #endif // NINJA_LEXER_H_ diff --git a/src/lexer.in.cc b/src/lexer.in.cc index 6f1d8e7937..60ecd07d26 100644 --- a/src/lexer.in.cc +++ b/src/lexer.in.cc @@ -21,6 +21,10 @@ using namespace std; +// $^ supported starting this version +const int minNewlineEscapeVersionMajor = 1; +const int minNewlineEscapeVersionMinor = 14; + bool Lexer::Error(const string& message, string* err) { // Compute line/column. int line = 1; @@ -259,6 +263,24 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { eval->AddText(StringPiece(":", 1)); continue; } + "$^" { + if (!newline_version_checked_) + { + if ((manifest_version_major < minNewlineEscapeVersionMajor) || + (manifest_version_major = minNewlineEscapeVersionMajor && + manifest_version_minor < minNewlineEscapeVersionMinor)) + { + return Error("using $^ escape requires specifying 'ninja_required_version' with version greater or equal 1.14", err); + } + newline_version_checked_ = true; + } +#ifdef _WIN32 + eval->AddText(StringPiece("\r\n", 2)); +#else + eval->AddText(StringPiece("\n", 1)); +#endif + continue; + } "$". { last_token_ = start; return Error("bad $-escape (literal $ must be written as $$)", err); diff --git a/src/lexer_test.cc b/src/lexer_test.cc index c5c416dc54..084dd782ab 100644 --- a/src/lexer_test.cc +++ b/src/lexer_test.cc @@ -96,3 +96,24 @@ TEST(Lexer, Tabs) { EXPECT_EQ(Lexer::ERROR, token); EXPECT_EQ("tabs are not allowed, use spaces", lexer.DescribeLastError()); } + +TEST(Lexer, EscapedNewlines) { + Lexer lexer("foo$\nbar$^newline foo\n"); + EvalString eval; + string err; + EXPECT_FALSE(lexer.ReadVarValue(&eval, &err)); + EXPECT_EQ("input:1: using $^ escape requires specifying 'ninja_required_version' with version greater or equal 1.14\n", err); + + lexer.manifest_version_major = 1; + lexer.manifest_version_minor = 14; + + eval.Clear(); + err = ""; + EXPECT_TRUE(lexer.ReadVarValue(&eval, &err)); + EXPECT_EQ("", err); +#ifdef _WIN32 + EXPECT_EQ("[foobar\r\nnewline foo]", eval.Serialize()); +#else + EXPECT_EQ("[foobar\nnewline foo]", eval.Serialize()); +#endif +} diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 30c4c151b5..9505753fe4 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -68,7 +68,7 @@ bool ManifestParser::Parse(const string& filename, const string& input, // Check ninja_required_version immediately so we can exit // before encountering any syntactic surprises. if (name == "ninja_required_version") - CheckNinjaVersion(value); + CheckNinjaVersion(value, &lexer_.manifest_version_major, &lexer_.manifest_version_minor); env_->AddBinding(name, value); break; } diff --git a/src/version.cc b/src/version.cc index 17e59e6050..beb6b1a57b 100644 --- a/src/version.cc +++ b/src/version.cc @@ -33,21 +33,21 @@ void ParseVersion(const string& version, int* major, int* minor) { } } -void CheckNinjaVersion(const string& version) { +void CheckNinjaVersion(const string& version, int* file_major, + int* file_minor) { int bin_major, bin_minor; ParseVersion(kNinjaVersion, &bin_major, &bin_minor); - int file_major, file_minor; - ParseVersion(version, &file_major, &file_minor); + ParseVersion(version, file_major, file_minor); - if (bin_major > file_major) { + if (bin_major > *file_major) { Warning("ninja executable version (%s) greater than build file " "ninja_required_version (%s); versions may be incompatible.", kNinjaVersion, version.c_str()); return; } - if ((bin_major == file_major && bin_minor < file_minor) || - bin_major < file_major) { + if ((bin_major == *file_major && bin_minor < *file_minor) || + bin_major < *file_major) { Fatal("ninja version (%s) incompatible with build file " "ninja_required_version version (%s).", kNinjaVersion, version.c_str()); diff --git a/src/version.h b/src/version.h index 9d84ecb1e9..192117feef 100644 --- a/src/version.h +++ b/src/version.h @@ -26,6 +26,7 @@ void ParseVersion(const std::string& version, int* major, int* minor); /// Check whether \a version is compatible with the current Ninja version, /// aborting if not. -void CheckNinjaVersion(const std::string& required_version); +void CheckNinjaVersion(const std::string& required_version, int* file_major, + int* file_minor); #endif // NINJA_VERSION_H_