1// Copyright 2015 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 "clparser.h" 16 17#include <algorithm> 18#include <assert.h> 19#include <string.h> 20 21#include "metrics.h" 22#include "string_piece_util.h" 23 24#ifdef _WIN32 25#include "includes_normalize.h" 26#include "string_piece.h" 27#else 28#include "util.h" 29#endif 30 31using namespace std; 32 33namespace { 34 35/// Return true if \a input ends with \a needle. 36bool EndsWith(const string& input, const string& needle) { 37 return (input.size() >= needle.size() && 38 input.substr(input.size() - needle.size()) == needle); 39} 40 41} // anonymous namespace 42 43// static 44string CLParser::FilterShowIncludes(const string& line, 45 const string& deps_prefix) { 46 const string kDepsPrefixEnglish = "Note: including file: "; 47 const char* in = line.c_str(); 48 const char* end = in + line.size(); 49 const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : deps_prefix; 50 if (end - in > (int)prefix.size() && 51 memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) { 52 in += prefix.size(); 53 while (*in == ' ') 54 ++in; 55 return line.substr(in - line.c_str()); 56 } 57 return ""; 58} 59 60// static 61bool CLParser::IsSystemInclude(string path) { 62 transform(path.begin(), path.end(), path.begin(), ToLowerASCII); 63 // TODO: this is a heuristic, perhaps there's a better way? 64 return (path.find("program files") != string::npos || 65 path.find("microsoft visual studio") != string::npos); 66} 67 68// static 69bool CLParser::FilterInputFilename(string line) { 70 transform(line.begin(), line.end(), line.begin(), ToLowerASCII); 71 // TODO: other extensions, like .asm? 72 return EndsWith(line, ".c") || 73 EndsWith(line, ".cc") || 74 EndsWith(line, ".cxx") || 75 EndsWith(line, ".cpp") || 76 EndsWith(line, ".c++"); 77} 78 79// static 80bool CLParser::Parse(const string& output, const string& deps_prefix, 81 string* filtered_output, string* err) { 82 METRIC_RECORD("CLParser::Parse"); 83 84 // Loop over all lines in the output to process them. 85 assert(&output != filtered_output); 86 size_t start = 0; 87 bool seen_show_includes = false; 88#ifdef _WIN32 89 IncludesNormalize normalizer("."); 90#endif 91 92 while (start < output.size()) { 93 size_t end = output.find_first_of("\r\n", start); 94 if (end == string::npos) 95 end = output.size(); 96 string line = output.substr(start, end - start); 97 98 string include = FilterShowIncludes(line, deps_prefix); 99 if (!include.empty()) { 100 seen_show_includes = true; 101 string normalized; 102#ifdef _WIN32 103 if (!normalizer.Normalize(include, &normalized, err)) 104 return false; 105#else 106 // TODO: should this make the path relative to cwd? 107 normalized = include; 108 uint64_t slash_bits; 109 CanonicalizePath(&normalized, &slash_bits); 110#endif 111 if (!IsSystemInclude(normalized)) 112 includes_.insert(normalized); 113 } else if (!seen_show_includes && FilterInputFilename(line)) { 114 // Drop it. 115 // TODO: if we support compiling multiple output files in a single 116 // cl.exe invocation, we should stash the filename. 117 } else { 118 filtered_output->append(line); 119 filtered_output->append("\n"); 120 } 121 122 if (end < output.size() && output[end] == '\r') 123 ++end; 124 if (end < output.size() && output[end] == '\n') 125 ++end; 126 start = end; 127 } 128 129 return true; 130} 131