Path: blob/main/libshaderc_util/src/message.cc
1560 views
// Copyright 2015 The Shaderc Authors. All rights reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#include "libshaderc_util/message.h"1516#include <algorithm>17#include <cstring>18#include <iostream>19#include <iterator>2021namespace shaderc_util {2223namespace {2425// Given a message, deduces and returns its type. If the message type is26// recognized, advances *message past the prefix indicating the type. Otherwise,27// leaves *message unchanged and returns MessageType::Unknown.28MessageType DeduceMessageType(string_piece* message) {29static const char kErrorMessage[] = "ERROR: ";30static const char kWarningMessage[] = "WARNING: ";31static const char kGlobalWarningMessage[] = "Warning, ";3233if (message->starts_with(kErrorMessage)) {34*message = message->substr(::strlen(kErrorMessage));35return MessageType::Error;36} else if (message->starts_with(kWarningMessage)) {37*message = message->substr(::strlen(kWarningMessage));38return MessageType::Warning;39} else if (message->starts_with(kGlobalWarningMessage)) {40*message = message->substr(::strlen(kGlobalWarningMessage));41return MessageType::GlobalWarning;42}43return MessageType::Unknown;44}4546// Deduces a location specification from the given message. A location47// specification is of the form "<source-name>:<line-number>:" and a trailing48// space. If the deduction is successful, returns true and updates source_name49// and line_number to the deduced source name and line numer respectively. The50// prefix standing for the location specification in message is skipped.51// Otherwise, returns false and keeps all parameters untouched.52bool DeduceLocationSpec(string_piece* message, string_piece* source_name,53string_piece* line_number) {54if (!message || message->empty()) {55return false;56}5758// When we find a pattern like this:59// colon60// digits61// colon62// space63// Then deduce that the source_name is the text before the first colon,64// the line number is the digits, and the message is the text after the65// second colon.6667const size_t size = message->size();68if (size <= 4) {69// A valid message must have a colon, a digit, a colon, and a space.70return false;71}72// The last possible position of the first colon.73const size_t first_colon_cutoff = size - 4;74// The last possible position of the second colon.75const size_t next_colon_cutoff = size - 2;7677for (size_t first_colon_pos = message->find_first_of(':'), next_colon_pos = 0;7879// There is a first colon, and it's not too close to the end80(first_colon_pos != string_piece::npos) &&81(first_colon_pos <= first_colon_cutoff);8283// Try the next pair of colons.84first_colon_pos = next_colon_pos) {85// We're guaranteed to have at least 3 more characters.86// Guarantee progress toward the end of the string.87next_colon_pos = message->find_first_of(':', first_colon_pos + 1);88if ((next_colon_pos == string_piece::npos) ||89(next_colon_pos > next_colon_cutoff)) {90// No valid solution.91return false;92}93if (first_colon_pos + 1 == next_colon_pos) {94// There is no room for digits.95continue;96}97if ((message->data()[next_colon_pos + 1] != ' ')) {98// There is no space character after the second colon.99continue;100}101if (message->find_first_not_of("0123456789", first_colon_pos + 1) ==102next_colon_pos) {103// We found the first solution.104*source_name = message->substr(0, first_colon_pos);105*line_number = message->substr(first_colon_pos + 1,106next_colon_pos - 1 - first_colon_pos);107*message = message->substr(next_colon_pos + 2);108return true;109}110}111112return false;113}114115// Returns true if the given message is a summary message.116bool IsSummaryMessage(const string_piece& message) {117const size_t space_loc = message.find_first_of(' ');118if (space_loc == string_piece::npos) return false;119const string_piece number = message.substr(0, space_loc);120const string_piece rest = message.substr(space_loc + 1);121if (!std::all_of(number.begin(), number.end(), ::isdigit)) return false;122if (!rest.starts_with("compilation errors.")) return false;123return true;124}125126} // anonymous namespace127128MessageType ParseGlslangOutput(const string_piece& message,129bool warnings_as_errors, bool suppress_warnings,130string_piece* source_name,131string_piece* line_number, string_piece* rest) {132string_piece rest_of_message(message);133source_name->clear();134line_number->clear();135rest->clear();136137// The glslang warning/error messages are typically of the following form:138// <message-type> <location-specification> <message-body>139//140// <message-type> can be "WARNING:", "ERROR:", or "Warning, ". "WARNING:"141// means a warning message for a certain line, while "Warning, " means a142// global one.143//144// <location-specification> is of the form:145// <filename-or-string-number>:<line-number>:146// It doesn't exist if the warning/error message is a global one.147//148// See Glslang's TInfoSink class implementation for details.149150bool is_error = false;151152// Handle <message-type>.153switch (DeduceMessageType(&rest_of_message)) {154case MessageType::Warning:155if (suppress_warnings) return MessageType::Ignored;156break;157case MessageType::Error:158is_error = true;159break;160case MessageType::GlobalWarning:161if (suppress_warnings) return MessageType::Ignored;162*rest = rest_of_message;163return warnings_as_errors ? MessageType::GlobalError164: MessageType::GlobalWarning;165case MessageType::Unknown:166*rest = rest_of_message;167return MessageType::Unknown;168default:169break;170}171172rest_of_message = rest_of_message.strip_whitespace();173if (rest_of_message.empty()) return MessageType::Unknown;174175// Now we have stripped the <message-type>. Try to see if we can find176// a <location-specification>.177if (DeduceLocationSpec(&rest_of_message, source_name, line_number)) {178*rest = rest_of_message;179return (is_error || warnings_as_errors) ? MessageType::Error180: MessageType::Warning;181} else {182// No <location-specification>. This is a global warning/error message.183// A special kind of global message is summary message, which should184// start with a number.185*rest = rest_of_message;186if (IsSummaryMessage(rest_of_message)) {187return (is_error || warnings_as_errors) ? MessageType::ErrorSummary188: MessageType::WarningSummary;189}190return (is_error || warnings_as_errors) ? MessageType::GlobalError191: MessageType::GlobalWarning;192}193return MessageType::Unknown;194}195196bool PrintFilteredErrors(const string_piece& file_name,197std::ostream* error_stream, bool warnings_as_errors,198bool suppress_warnings, const char* error_list,199size_t* total_warnings, size_t* total_errors) {200const char* ignored_error_strings[] = {201"Warning, version 310 is not yet complete; most version-specific "202"features are present, but some are missing.",203"Warning, version 400 is not yet complete; most version-specific "204"features are present, but some are missing.",205"Warning, version 410 is not yet complete; most version-specific "206"features are present, but some are missing.",207"Warning, version 420 is not yet complete; most version-specific "208"features are present, but some are missing.",209"Warning, version 430 is not yet complete; most version-specific "210"features are present, but some are missing.",211"Warning, version 440 is not yet complete; most version-specific "212"features are present, but some are missing.",213"Warning, version 450 is not yet complete; most version-specific "214"features are present, but some are missing.",215"Linked vertex stage:", "Linked fragment stage:",216"Linked tessellation control stage:",217"Linked tessellation evaluation stage:", "Linked geometry stage:",218"Linked compute stage:", ""};219size_t existing_total_errors = *total_errors;220string_piece error_messages(error_list);221for (const string_piece& message : error_messages.get_fields('\n')) {222if (std::find(std::begin(ignored_error_strings),223std::end(ignored_error_strings),224message) == std::end(ignored_error_strings)) {225string_piece source_name;226string_piece line_number;227string_piece rest;228const MessageType type =229ParseGlslangOutput(message, warnings_as_errors, suppress_warnings,230&source_name, &line_number, &rest);231string_piece name = file_name;232if (!source_name.empty()) {233// -1 is the string number for the preamble injected by us.234name = source_name == "-1" ? "<command line>" : source_name;235}236switch (type) {237case MessageType::Error:238case MessageType::Warning:239assert(!name.empty() && !line_number.empty() && !rest.empty());240*error_stream << name << ":" << line_number << ": "241<< (type == MessageType::Error ? "error: "242: "warning: ")243<< rest.strip_whitespace() << std::endl;244*total_errors += type == MessageType::Error;245*total_warnings += type == MessageType::Warning;246break;247case MessageType::ErrorSummary:248case MessageType::WarningSummary:249break;250case MessageType::GlobalError:251case MessageType::GlobalWarning:252assert(!rest.empty());253*total_errors += type == MessageType::GlobalError;254*total_warnings += type == MessageType::GlobalWarning;255*error_stream << name << ": "256<< (type == MessageType::GlobalError ? "error"257: "warning")258<< ": " << rest.strip_whitespace() << std::endl;259break;260case MessageType::Unknown:261*error_stream << name << ":";262*error_stream << " " << message << std::endl;263break;264case MessageType::Ignored:265break;266}267}268}269return (existing_total_errors == *total_errors);270}271272// Outputs the number of warnings and errors if there are any.273void OutputMessages(std::ostream* error_stream, size_t total_warnings,274size_t total_errors) {275if (total_warnings > 0 || total_errors > 0) {276if (total_warnings > 0 && total_errors > 0) {277*error_stream << total_warnings << " warning"278<< (total_warnings > 1 ? "s" : "") << " and "279<< total_errors << " error" << (total_errors > 1 ? "s" : "")280<< " generated." << std::endl;281} else if (total_warnings > 0) {282*error_stream << total_warnings << " warning"283<< (total_warnings > 1 ? "s" : "") << " generated."284<< std::endl;285} else if (total_errors > 0) {286*error_stream << total_errors << " error" << (total_errors > 1 ? "s" : "")287<< " generated." << std::endl;288}289}290}291292} // namespace glslc293294295