Ninja
depfile_parser.in.cc
Go to the documentation of this file.
1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "depfile_parser.h"
16 #include "util.h"
17 
19  : options_(options)
20 {
21 }
22 
23 // A note on backslashes in Makefiles, from reading the docs:
24 // Backslash-newline is the line continuation character.
25 // Backslash-# escapes a # (otherwise meaningful as a comment start).
26 // Backslash-% escapes a % (otherwise meaningful as a special).
27 // Finally, quoting the GNU manual, "Backslashes that are not in danger
28 // of quoting ‘%’ characters go unmolested."
29 // How do you end a line with a backslash? The netbsd Make docs suggest
30 // reading the result of a shell command echoing a backslash!
31 //
32 // Rather than implement all of above, we do a simpler thing here:
33 // Backslashes escape a set of characters (see "escapes" defined below),
34 // otherwise they are passed through verbatim.
35 // If anyone actually has depfiles that rely on the more complicated
36 // behavior we can adjust this.
37 bool DepfileParser::Parse(string* content, string* err) {
38  // in: current parser input point.
39  // end: end of input.
40  // parsing_targets: whether we are parsing targets or dependencies.
41  char* in = &(*content)[0];
42  char* end = in + content->size();
43  bool have_target = false;
44  bool have_secondary_target_on_this_rule = false;
45  bool have_newline_since_primary_target = false;
46  bool warned_distinct_target_lines = false;
47  bool parsing_targets = true;
48  while (in < end) {
49  bool have_newline = false;
50  // out: current output point (typically same as in, but can fall behind
51  // as we de-escape backslashes).
52  char* out = in;
53  // filename: start of the current parsed filename.
54  char* filename = out;
55  for (;;) {
56  // start: beginning of the current parsed span.
57  const char* start = in;
58  char* yymarker = NULL;
59  /*!re2c
60  re2c:define:YYCTYPE = "unsigned char";
61  re2c:define:YYCURSOR = in;
62  re2c:define:YYLIMIT = end;
63  re2c:define:YYMARKER = yymarker;
64 
65  re2c:yyfill:enable = 0;
66 
67  re2c:indent:top = 2;
68  re2c:indent:string = " ";
69 
70  nul = "\000";
71  escape = [ \\#*[|\]];
72  newline = '\r'?'\n';
73 
74  '\\' escape {
75  // De-escape backslashed character.
76  *out++ = yych;
77  continue;
78  }
79  '$$' {
80  // De-escape dollar character.
81  *out++ = '$';
82  continue;
83  }
84  '\\' [^\000\r\n] {
85  // Let backslash before other characters through verbatim.
86  *out++ = '\\';
87  *out++ = yych;
88  continue;
89  }
90  [a-zA-Z0-9+,/_:.~()}{%@=!\x80-\xFF-]+ {
91  // Got a span of plain text.
92  int len = (int)(in - start);
93  // Need to shift it over if we're overwriting backslashes.
94  if (out < start)
95  memmove(out, start, len);
96  out += len;
97  continue;
98  }
99  nul {
100  break;
101  }
102  '\\' newline {
103  // A line continuation ends the current file name.
104  break;
105  }
106  newline {
107  // A newline ends the current file name and the current rule.
108  have_newline = true;
109  break;
110  }
111  [^] {
112  // For any other character (e.g. whitespace), swallow it here,
113  // allowing the outer logic to loop around again.
114  break;
115  }
116  */
117  }
118 
119  int len = (int)(out - filename);
120  const bool is_dependency = !parsing_targets;
121  if (len > 0 && filename[len - 1] == ':') {
122  len--; // Strip off trailing colon, if any.
123  parsing_targets = false;
124  have_target = true;
125  }
126 
127  if (len > 0) {
128  if (is_dependency) {
129  if (have_secondary_target_on_this_rule) {
130  if (!have_newline_since_primary_target) {
131  *err = "depfile has multiple output paths";
132  return false;
135  *err =
136  "depfile has multiple output paths (on separate lines)"
137  " [-w depfilemulti=err]";
138  return false;
139  } else {
140  if (!warned_distinct_target_lines) {
141  warned_distinct_target_lines = true;
142  Warning("depfile has multiple output paths (on separate lines); "
143  "continuing anyway [-w depfilemulti=warn]");
144  }
145  continue;
146  }
147  }
148  ins_.push_back(StringPiece(filename, len));
149  } else if (!out_.str_) {
150  out_ = StringPiece(filename, len);
151  } else if (out_ != StringPiece(filename, len)) {
152  have_secondary_target_on_this_rule = true;
153  }
154  }
155 
156  if (have_newline) {
157  // A newline ends a rule so the next filename will be a new target.
158  parsing_targets = true;
159  have_secondary_target_on_this_rule = false;
160  if (have_target) {
161  have_newline_since_primary_target = true;
162  }
163  }
164  }
165  if (!have_target) {
166  *err = "expected ':' in depfile";
167  return false;
168  }
169  return true;
170 }
const char * str_
Definition: string_piece.h:67
StringPiece represents a slice of a string whose memory is managed externally.
Definition: string_piece.h:27
bool Parse(string *content, string *err)
Parse an input file.
DepfileParserOptions options_
DepfileDistinctTargetLinesAction depfile_distinct_target_lines_action_
vector< StringPiece > ins_
StringPiece out_
DepfileParser(DepfileParserOptions options=DepfileParserOptions())
void Warning(const char *msg,...)
Log a warning message.
Definition: util.cc:75