| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "clparser.h" |
| |
| #include <algorithm> |
| #include <assert.h> |
| #include <string.h> |
| |
| #include "metrics.h" |
| #include "string_piece_util.h" |
| |
| #ifdef _WIN32 |
| #include "includes_normalize.h" |
| #include "string_piece.h" |
| #else |
| #include "util.h" |
| #endif |
| |
| using namespace std; |
| |
| namespace { |
| |
| /// Return true if \a input ends with \a needle. |
| bool EndsWith(const string& input, const string& needle) { |
| return (input.size() >= needle.size() && |
| input.substr(input.size() - needle.size()) == needle); |
| } |
| |
| } // anonymous namespace |
| |
| // static |
| string CLParser::FilterShowIncludes(const string& line, |
| const string& deps_prefix) { |
| const string kDepsPrefixEnglish = "Note: including file: "; |
| const char* in = line.c_str(); |
| const char* end = in + line.size(); |
| const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : 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()); |
| } |
| return ""; |
| } |
| |
| // static |
| bool CLParser::IsSystemInclude(string path) { |
| transform(path.begin(), path.end(), path.begin(), ToLowerASCII); |
| // TODO: this is a heuristic, perhaps there's a better way? |
| return (path.find("program files") != string::npos || |
| path.find("microsoft visual studio") != string::npos); |
| } |
| |
| // static |
| bool CLParser::FilterInputFilename(string line) { |
| transform(line.begin(), line.end(), line.begin(), ToLowerASCII); |
| // TODO: other extensions, like .asm? |
| return EndsWith(line, ".c") || |
| EndsWith(line, ".cc") || |
| EndsWith(line, ".cxx") || |
| EndsWith(line, ".cpp"); |
| } |
| |
| // static |
| bool CLParser::Parse(const string& output, const string& deps_prefix, |
| string* filtered_output, string* err) { |
| METRIC_RECORD("CLParser::Parse"); |
| |
| // Loop over all lines in the output to process them. |
| assert(&output != filtered_output); |
| size_t start = 0; |
| #ifdef _WIN32 |
| IncludesNormalize normalizer("."); |
| #endif |
| |
| while (start < output.size()) { |
| size_t end = output.find_first_of("\r\n", start); |
| if (end == string::npos) |
| end = output.size(); |
| string line = output.substr(start, end - start); |
| |
| string include = FilterShowIncludes(line, deps_prefix); |
| if (!include.empty()) { |
| string normalized; |
| #ifdef _WIN32 |
| if (!normalizer.Normalize(include, &normalized, err)) |
| return false; |
| #else |
| // TODO: should this make the path relative to cwd? |
| normalized = include; |
| uint64_t slash_bits; |
| if (!CanonicalizePath(&normalized, &slash_bits, err)) |
| return false; |
| #endif |
| if (!IsSystemInclude(normalized)) |
| includes_.insert(normalized); |
| } else if (FilterInputFilename(line)) { |
| // Drop it. |
| // TODO: if we support compiling multiple output files in a single |
| // cl.exe invocation, we should stash the filename. |
| } else { |
| filtered_output->append(line); |
| filtered_output->append("\n"); |
| } |
| |
| if (end < output.size() && output[end] == '\r') |
| ++end; |
| if (end < output.size() && output[end] == '\n') |
| ++end; |
| start = end; |
| } |
| |
| return true; |
| } |