Ninja
depfile_parser.in.cc
Go to the documentation of this file.
00001 // Copyright 2011 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //     http://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "depfile_parser.h"
00016 
00017 // A note on backslashes in Makefiles, from reading the docs:
00018 // Backslash-newline is the line continuation character.
00019 // Backslash-# escapes a # (otherwise meaningful as a comment start).
00020 // Backslash-% escapes a % (otherwise meaningful as a special).
00021 // Finally, quoting the GNU manual, "Backslashes that are not in danger
00022 // of quoting ‘%’ characters go unmolested."
00023 // How do you end a line with a backslash?  The netbsd Make docs suggest
00024 // reading the result of a shell command echoing a backslash!
00025 //
00026 // Rather than implement all of above, we do a simpler thing here:
00027 // Backslashes escape a set of characters (see "escapes" defined below),
00028 // otherwise they are passed through verbatim.
00029 // If anyone actually has depfiles that rely on the more complicated
00030 // behavior we can adjust this.
00031 bool DepfileParser::Parse(string* content, string* err) {
00032   // in: current parser input point.
00033   // end: end of input.
00034   // parsing_targets: whether we are parsing targets or dependencies.
00035   char* in = &(*content)[0];
00036   char* end = in + content->size();
00037   bool parsing_targets = true;
00038   while (in < end) {
00039     // out: current output point (typically same as in, but can fall behind
00040     // as we de-escape backslashes).
00041     char* out = in;
00042     // filename: start of the current parsed filename.
00043     char* filename = out;
00044     for (;;) {
00045       // start: beginning of the current parsed span.
00046       const char* start = in;
00047       /*!re2c
00048       re2c:define:YYCTYPE = "char";
00049       re2c:define:YYCURSOR = in;
00050       re2c:define:YYLIMIT = end;
00051 
00052       re2c:yyfill:enable = 0;
00053 
00054       re2c:indent:top = 2;
00055       re2c:indent:string = "  ";
00056 
00057       nul = "\000";
00058       escape = [ \\#*[|];
00059 
00060       '\\' escape {
00061         // De-escape backslashed character.
00062         *out++ = yych;
00063         continue;
00064       }
00065       '$$' {
00066         // De-escape dollar character.
00067         *out++ = '$';
00068         continue;
00069       }
00070       '\\' [^\000\n] {
00071         // Let backslash before other characters through verbatim.
00072         *out++ = '\\';
00073         *out++ = yych;
00074         continue;
00075       }
00076       [a-zA-Z0-9+,/_:.~()@=-!]+ {
00077         // Got a span of plain text.
00078         int len = (int)(in - start);
00079         // Need to shift it over if we're overwriting backslashes.
00080         if (out < start)
00081           memmove(out, start, len);
00082         out += len;
00083         continue;
00084       }
00085       nul {
00086         break;
00087       }
00088       [^] {
00089         // For any other character (e.g. whitespace), swallow it here,
00090         // allowing the outer logic to loop around again.
00091         break;
00092       }
00093       */
00094     }
00095 
00096     int len = (int)(out - filename);
00097     const bool is_target = parsing_targets;
00098     if (len > 0 && filename[len - 1] == ':') {
00099       len--;  // Strip off trailing colon, if any.
00100       parsing_targets = false;
00101     }
00102 
00103     if (len == 0)
00104       continue;
00105 
00106     if (!is_target) {
00107       ins_.push_back(StringPiece(filename, len));
00108     } else if (!out_.str_) {
00109       out_ = StringPiece(filename, len);
00110     } else if (out_ != StringPiece(filename, len)) {
00111       *err = "depfile has multiple output paths.";
00112       return false;
00113     }
00114   }
00115   return true;
00116 }