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
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
// 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");
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));
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_;
} // 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));
}
// 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());
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.
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))
/// 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.
/// 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_;
};
"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"
);
}
{ 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;
case 'o':
output_filename = optarg;
break;
+ case 'p':
+ deps_prefix = optarg;
+ break;
case 'h':
default:
Usage();
if (output_filename) {
CLParser parser;
- output = parser.Parse(output);
+ output = parser.Parse(output, deps_prefix);
WriteDepFileOrDie(output_filename, parser);
}
#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) {
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());
CLParser parser;
string output = parser.Parse(
"foo.cc\r\n"
- "cl: warning\r\n");
+ "cl: warning\r\n",
+ "");
ASSERT_EQ("cl: warning\n", output);
}
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);
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());
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());