add deps_prefix for localized /showIncludes' output parsing
authorPeter Kümmel <kuemmel@coffeelogic.de>
Sun, 13 Oct 2013 10:20:17 +0000 (12:20 +0200)
committerPeter Kümmel <syntheticpp@gmx.net>
Fri, 18 Oct 2013 23:25:47 +0000 (01:25 +0200)
doc/manual.asciidoc
src/build.cc
src/build.h
src/includes_normalize_test.cc
src/msvc_helper-win32.cc
src/msvc_helper.h
src/msvc_helper_main-win32.cc
src/msvc_helper_test.cc

index a735257..67fcbfd 100644 (file)
@@ -580,9 +580,13 @@ Ninja supports this processing in two forms.
    http://msdn.microsoft.com/en-us/library/hdkef6tk(v=vs.90).aspx[`/showIncludes`
    flag].  Briefly, this means the tool outputs specially-formatted lines
    to its stdout.  Ninja then filters these lines from the displayed
-   output.  No `depfile` attribute is necessary.
+   output.  No `depfile` attribute is necessary, but the localized string
+   in front of the the header file path. For instance
+   `msvc_deps_prefix = Note: including file: `
+   for a English Visual Studio (the default). Should be globally defined.
 +
 ----
+msvc_deps_prefix = Note: including file:
 rule cc
   deps = msvc
   command = cl /showIncludes -c $in /Fo$out
@@ -772,6 +776,10 @@ keys.
    stored as `.ninja_deps` in the `builddir`, see <<ref_toplevel,the
    discussion of `builddir`>>.
 
+`msvc_deps_prefix`:: _(Available since Ninja 1.5.)_ defines the string
+  which should be stripped from msvc's /showIncludes output. Only
+  needed when `deps = msvc` and no English Visual Studio version is used.
+
 `description`:: a short description of the command, used to pretty-print
   the command as it's running.  The `-v` flag controls whether to print
   the full command or its description; if a command fails, the full command
index 9718f85..33aa85b 100644 (file)
@@ -714,9 +714,10 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
   // build perspective.
   vector<Node*> deps_nodes;
   string deps_type = edge->GetBinding("deps");
+  const string deps_prefix = edge->GetBinding("msvc_deps_prefix");
   if (!deps_type.empty()) {
     string extract_err;
-    if (!ExtractDeps(result, deps_type, &deps_nodes, &extract_err) &&
+    if (!ExtractDeps(result, deps_type, deps_prefix, &deps_nodes, &extract_err) &&
         result->success()) {
       if (!result->output.empty())
         result->output.append("\n");
@@ -802,12 +803,13 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
 
 bool Builder::ExtractDeps(CommandRunner::Result* result,
                           const string& deps_type,
+                          const string& deps_prefix,
                           vector<Node*>* deps_nodes,
                           string* err) {
 #ifdef _WIN32
   if (deps_type == "msvc") {
     CLParser parser;
-    result->output = parser.Parse(result->output);
+    result->output = parser.Parse(result->output, deps_prefix);
     for (set<string>::iterator i = parser.includes_.begin();
          i != parser.includes_.end(); ++i) {
       deps_nodes->push_back(state_->GetNode(*i));
index 5b6c83c..1122d84 100644 (file)
@@ -180,7 +180,7 @@ struct Builder {
   BuildStatus* status_;
 
  private:
-  bool ExtractDeps(CommandRunner::Result* result, const string& deps_type,
+  bool ExtractDeps(CommandRunner::Result* result, const string& deps_type, const string& deps_prefix,
                    vector<Node*>* deps_nodes, string* err);
 
   DiskInterface* disk_interface_;
index 1713d5d..419996f 100644 (file)
@@ -38,7 +38,7 @@ string GetCurDir() {
 }  // namespace
 
 TEST(IncludesNormalize, WithRelative) {
-  string currentdir = IncludesNormalize::ToLower(GetCurDir());
+  string currentdir = GetCurDir();
   EXPECT_EQ("c", IncludesNormalize::Normalize("a/b/c", "a/b"));
   EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"),
                                               NULL));
index 7c45029..3065ab0 100644 (file)
@@ -48,14 +48,13 @@ string EscapeForDepfile(const string& path) {
 }
 
 // static
-string CLParser::FilterShowIncludes(const string& line) {
-  static const char kMagicPrefix[] = "Note: including file: ";
+string CLParser::FilterShowIncludes(const string& line, const string& deps_prefix) {
+  static const string deps_prefix_english = "Note: including file: ";
   const char* in = line.c_str();
   const char* end = in + line.size();
-
-  if (end - in > (int)sizeof(kMagicPrefix) - 1 &&
-      memcmp(in, kMagicPrefix, sizeof(kMagicPrefix) - 1) == 0) {
-    in += sizeof(kMagicPrefix) - 1;
+  const string& prefix = deps_prefix.empty() ? deps_prefix_english : deps_prefix;
+  if (end - in > (int)prefix.size() && memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) {
+    in += prefix.size();
     while (*in == ' ')
       ++in;
     return line.substr(in - line.c_str());
@@ -81,7 +80,7 @@ bool CLParser::FilterInputFilename(string line) {
       EndsWith(line, ".cpp");
 }
 
-string CLParser::Parse(const string& output) {
+string CLParser::Parse(const string& output, const string& deps_prefix) {
   string filtered_output;
 
   // Loop over all lines in the output to process them.
@@ -92,7 +91,7 @@ string CLParser::Parse(const string& output) {
       end = output.size();
     string line = output.substr(start, end - start);
 
-    string include = FilterShowIncludes(line);
+    string include = FilterShowIncludes(line, deps_prefix);
     if (!include.empty()) {
       include = IncludesNormalize::Normalize(include, NULL);
       if (!IsSystemInclude(include))
index e207485..0433769 100644 (file)
@@ -27,7 +27,7 @@ struct CLParser {
   /// Parse a line of cl.exe output and extract /showIncludes info.
   /// If a dependency is extracted, returns a nonempty string.
   /// Exposed for testing.
-  static string FilterShowIncludes(const string& line);
+  static string FilterShowIncludes(const string& line, const string& deps_prefix);
 
   /// Return true if a mentioned include file is a system path.
   /// Filtering these out reduces dependency information considerably.
@@ -41,7 +41,7 @@ struct CLParser {
 
   /// Parse the full output of cl, returning the output (if any) that
   /// should printed.
-  string Parse(const string& output);
+  string Parse(const string& output, const string& deps_prefix);
 
   set<string> includes_;
 };
index e3a7846..58bc797 100644 (file)
@@ -31,6 +31,7 @@ void Usage() {
 "options:\n"
 "  -e ENVFILE load environment block from ENVFILE as environment\n"
 "  -o FILE    write output dependency information to FILE.d\n"
+"  -p STRING  localized prefix of msvc's /showIncludes output\n"
          );
 }
 
@@ -84,7 +85,8 @@ int MSVCHelperMain(int argc, char** argv) {
     { NULL, 0, NULL, 0 }
   };
   int opt;
-  while ((opt = getopt_long(argc, argv, "e:o:h", kLongOptions, NULL)) != -1) {
+  string deps_prefix;
+  while ((opt = getopt_long(argc, argv, "e:o:p:h", kLongOptions, NULL)) != -1) {
     switch (opt) {
       case 'e':
         envfile = optarg;
@@ -92,6 +94,9 @@ int MSVCHelperMain(int argc, char** argv) {
       case 'o':
         output_filename = optarg;
         break;
+      case 'p':
+        deps_prefix = optarg;
+        break;
       case 'h':
       default:
         Usage();
@@ -122,7 +127,7 @@ int MSVCHelperMain(int argc, char** argv) {
 
   if (output_filename) {
     CLParser parser;
-    output = parser.Parse(output);
+    output = parser.Parse(output, deps_prefix);
     WriteDepFileOrDie(output_filename, parser);
   }
 
index 02f2863..48fbe21 100644 (file)
 #include "util.h"
 
 TEST(CLParserTest, ShowIncludes) {
-  ASSERT_EQ("", CLParser::FilterShowIncludes(""));
+  ASSERT_EQ("", CLParser::FilterShowIncludes("", ""));
 
-  ASSERT_EQ("", CLParser::FilterShowIncludes("Sample compiler output"));
+  ASSERT_EQ("", CLParser::FilterShowIncludes("Sample compiler output", ""));
   ASSERT_EQ("c:\\Some Files\\foobar.h",
             CLParser::FilterShowIncludes("Note: including file: "
-                                         "c:\\Some Files\\foobar.h"));
+                                         "c:\\Some Files\\foobar.h", ""));
   ASSERT_EQ("c:\\initspaces.h",
             CLParser::FilterShowIncludes("Note: including file:    "
-                                         "c:\\initspaces.h"));
+                                         "c:\\initspaces.h", ""));
+  ASSERT_EQ("c:\\initspaces.h",
+            CLParser::FilterShowIncludes("Non-default prefix: inc file:    "
+                                         "c:\\initspaces.h",
+                    "Non-default prefix: inc file:"));
 }
 
 TEST(CLParserTest, FilterInputFilename) {
@@ -46,8 +50,9 @@ TEST(CLParserTest, ParseSimple) {
   CLParser parser;
   string output = parser.Parse(
       "foo\r\n"
-      "Note: including file:  foo.h\r\n"
-      "bar\r\n");
+      "Note: inc file prefix:  foo.h\r\n"
+      "bar\r\n",
+      "Note: inc file prefix:");
 
   ASSERT_EQ("foo\nbar\n", output);
   ASSERT_EQ(1u, parser.includes_.size());
@@ -58,7 +63,8 @@ TEST(CLParserTest, ParseFilenameFilter) {
   CLParser parser;
   string output = parser.Parse(
       "foo.cc\r\n"
-      "cl: warning\r\n");
+      "cl: warning\r\n",
+      "");
   ASSERT_EQ("cl: warning\n", output);
 }
 
@@ -67,7 +73,8 @@ TEST(CLParserTest, ParseSystemInclude) {
   string output = parser.Parse(
       "Note: including file: c:\\Program Files\\foo.h\r\n"
       "Note: including file: d:\\Microsoft Visual Studio\\bar.h\r\n"
-      "Note: including file: path.h\r\n");
+      "Note: including file: path.h\r\n",
+      "");
   // We should have dropped the first two includes because they look like
   // system headers.
   ASSERT_EQ("", output);
@@ -80,7 +87,8 @@ TEST(CLParserTest, DuplicatedHeader) {
   string output = parser.Parse(
       "Note: including file: foo.h\r\n"
       "Note: including file: bar.h\r\n"
-      "Note: including file: foo.h\r\n");
+      "Note: including file: foo.h\r\n",
+      "");
   // We should have dropped one copy of foo.h.
   ASSERT_EQ("", output);
   ASSERT_EQ(2u, parser.includes_.size());
@@ -91,7 +99,8 @@ TEST(CLParserTest, DuplicatedHeaderPathConverted) {
   string output = parser.Parse(
       "Note: including file: sub/foo.h\r\n"
       "Note: including file: bar.h\r\n"
-      "Note: including file: sub\\foo.h\r\n");
+      "Note: including file: sub\\foo.h\r\n",
+      "");
   // We should have dropped one copy of foo.h.
   ASSERT_EQ("", output);
   ASSERT_EQ(2u, parser.includes_.size());