// Copyright 2015 The Shaderc Authors. 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 "libshaderc_util/message.h" #include #include #include #include namespace shaderc_util { namespace { // Given a message, deduces and returns its type. If the message type is // recognized, advances *message past the prefix indicating the type. Otherwise, // leaves *message unchanged and returns MessageType::Unknown. MessageType DeduceMessageType(string_piece* message) { static const char kErrorMessage[] = "ERROR: "; static const char kWarningMessage[] = "WARNING: "; static const char kGlobalWarningMessage[] = "Warning, "; if (message->starts_with(kErrorMessage)) { *message = message->substr(::strlen(kErrorMessage)); return MessageType::Error; } else if (message->starts_with(kWarningMessage)) { *message = message->substr(::strlen(kWarningMessage)); return MessageType::Warning; } else if (message->starts_with(kGlobalWarningMessage)) { *message = message->substr(::strlen(kGlobalWarningMessage)); return MessageType::GlobalWarning; } return MessageType::Unknown; } // Deduces a location specification from the given message. A location // specification is of the form "::". If the deduction // is successful, returns true and updates source_name and line_number to the // deduced source name and line numer respectively. The prefix standing for the // location specification in message is skipped. Otherwise, returns false and // keeps all parameters untouched. bool DeduceLocationSpec(string_piece* message, string_piece* source_name, string_piece* line_number) { // TODO(antiagainst): we use ':' as a delimiter here. It may be a valid // character in the filename. Also be aware of other special characters, // for example, ' '. string_piece rest(*message); size_t colon_after_source = rest.find_first_of(':'); if (colon_after_source == string_piece::npos) return false; string_piece source = rest.substr(0, colon_after_source); rest = rest.substr(colon_after_source + 1); size_t colon_after_line = rest.find_first_of(':'); if (source.size() == 1 && ::isalpha(source.front()) && rest.size() > 0 && rest.front() == '\\') { // Handle Windows path. colon_after_source += colon_after_line + 1; source = message->substr(0, colon_after_source); rest = rest.substr(colon_after_line + 1); colon_after_line = rest.find_first_of(':'); } if (colon_after_line == string_piece::npos) return false; const string_piece line = rest.substr(0, colon_after_line); if (!std::all_of(line.begin(), line.end(), ::isdigit)) return false; *source_name = source; *line_number = line; *message = rest.substr(colon_after_line + 1).strip_whitespace(); return true; } // Returns true if the given message is a summary message. bool IsSummaryMessage(const string_piece& message) { const size_t space_loc = message.find_first_of(' '); if (space_loc == string_piece::npos) return false; const string_piece number = message.substr(0, space_loc); const string_piece rest = message.substr(space_loc + 1); if (!std::all_of(number.begin(), number.end(), ::isdigit)) return false; if (!rest.starts_with("compilation errors.")) return false; return true; } } // anonymous namespace MessageType ParseGlslangOutput(const string_piece& message, bool warnings_as_errors, bool suppress_warnings, string_piece* source_name, string_piece* line_number, string_piece* rest) { string_piece rest_of_message(message); source_name->clear(); line_number->clear(); rest->clear(); // The glslang warning/error messages are typically of the following form: // // // can be "WARNING:", "ERROR:", or "Warning, ". "WARNING:" // means a warning message for a certain line, while "Warning, " means a // global one. // // is of the form: // :: // It doesn't exist if the warning/error message is a global one. bool is_error = false; // Handle . switch (DeduceMessageType(&rest_of_message)) { case MessageType::Warning: if (suppress_warnings) return MessageType::Ignored; break; case MessageType::Error: is_error = true; break; case MessageType::GlobalWarning: if (suppress_warnings) return MessageType::Ignored; *rest = rest_of_message; return warnings_as_errors ? MessageType::GlobalError : MessageType::GlobalWarning; case MessageType::Unknown: *rest = rest_of_message; return MessageType::Unknown; default: break; } rest_of_message = rest_of_message.strip_whitespace(); if (rest_of_message.empty()) return MessageType::Unknown; // Now we have stripped the . Try to see if we can find // a . if (DeduceLocationSpec(&rest_of_message, source_name, line_number)) { *rest = rest_of_message; return (is_error || warnings_as_errors) ? MessageType::Error : MessageType::Warning; } else { // No . This is a global warning/error message. // A special kind of global message is summary message, which should // start with a number. *rest = rest_of_message; if (IsSummaryMessage(rest_of_message)) { return (is_error || warnings_as_errors) ? MessageType::ErrorSummary : MessageType::WarningSummary; } return (is_error || warnings_as_errors) ? MessageType::GlobalError : MessageType::GlobalWarning; } return MessageType::Unknown; } bool PrintFilteredErrors(const string_piece& file_name, std::ostream* error_stream, bool warnings_as_errors, bool suppress_warnings, const char* error_list, size_t* total_warnings, size_t* total_errors) { const char* ignored_error_strings[] = { "Warning, version 310 is not yet complete; most version-specific " "features are present, but some are missing.", "Warning, version 400 is not yet complete; most version-specific " "features are present, but some are missing.", "Warning, version 410 is not yet complete; most version-specific " "features are present, but some are missing.", "Warning, version 420 is not yet complete; most version-specific " "features are present, but some are missing.", "Warning, version 430 is not yet complete; most version-specific " "features are present, but some are missing.", "Warning, version 440 is not yet complete; most version-specific " "features are present, but some are missing.", "Warning, version 450 is not yet complete; most version-specific " "features are present, but some are missing.", "Linked vertex stage:", "Linked fragment stage:", "Linked tessellation control stage:", "Linked tessellation evaluation stage:", "Linked geometry stage:", "Linked compute stage:", ""}; size_t existing_total_errors = *total_errors; string_piece error_messages(error_list); for (const string_piece& message : error_messages.get_fields('\n')) { if (std::find(std::begin(ignored_error_strings), std::end(ignored_error_strings), message) == std::end(ignored_error_strings)) { string_piece source_name; string_piece line_number; string_piece rest; const MessageType type = ParseGlslangOutput(message, warnings_as_errors, suppress_warnings, &source_name, &line_number, &rest); string_piece name = file_name; if (!source_name.empty()) { // -1 is the string number for the preamble injected by us. name = source_name == "-1" ? "" : source_name; } switch (type) { case MessageType::Error: case MessageType::Warning: assert(!name.empty() && !line_number.empty() && !rest.empty()); *error_stream << name << ":" << line_number << ": " << (type == MessageType::Error ? "error: " : "warning: ") << rest.strip_whitespace() << std::endl; *total_errors += type == MessageType::Error; *total_warnings += type == MessageType::Warning; break; case MessageType::ErrorSummary: case MessageType::WarningSummary: break; case MessageType::GlobalError: case MessageType::GlobalWarning: assert(!rest.empty()); *total_errors += type == MessageType::GlobalError; *total_warnings += type == MessageType::GlobalWarning; *error_stream << name << ": " << (type == MessageType::GlobalError ? "error" : "warning") << ": " << rest.strip_whitespace() << std::endl; break; case MessageType::Unknown: *error_stream << name << ":"; *error_stream << " " << message << std::endl; break; case MessageType::Ignored: break; } } } return (existing_total_errors == *total_errors); } // Outputs the number of warnings and errors if there are any. void OutputMessages(std::ostream* error_stream, size_t total_warnings, size_t total_errors) { if (total_warnings > 0 || total_errors > 0) { if (total_warnings > 0 && total_errors > 0) { *error_stream << total_warnings << " warning" << (total_warnings > 1 ? "s" : "") << " and " << total_errors << " error" << (total_errors > 1 ? "s" : "") << " generated." << std::endl; } else if (total_warnings > 0) { *error_stream << total_warnings << " warning" << (total_warnings > 1 ? "s" : "") << " generated." << std::endl; } else if (total_errors > 0) { *error_stream << total_errors << " error" << (total_errors > 1 ? "s" : "") << " generated." << std::endl; } } } } // namespace glslc