Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/shaderc
Path: blob/main/libshaderc_util/src/message.cc
1560 views
1
// Copyright 2015 The Shaderc Authors. 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 "libshaderc_util/message.h"
16
17
#include <algorithm>
18
#include <cstring>
19
#include <iostream>
20
#include <iterator>
21
22
namespace shaderc_util {
23
24
namespace {
25
26
// Given a message, deduces and returns its type. If the message type is
27
// recognized, advances *message past the prefix indicating the type. Otherwise,
28
// leaves *message unchanged and returns MessageType::Unknown.
29
MessageType DeduceMessageType(string_piece* message) {
30
static const char kErrorMessage[] = "ERROR: ";
31
static const char kWarningMessage[] = "WARNING: ";
32
static const char kGlobalWarningMessage[] = "Warning, ";
33
34
if (message->starts_with(kErrorMessage)) {
35
*message = message->substr(::strlen(kErrorMessage));
36
return MessageType::Error;
37
} else if (message->starts_with(kWarningMessage)) {
38
*message = message->substr(::strlen(kWarningMessage));
39
return MessageType::Warning;
40
} else if (message->starts_with(kGlobalWarningMessage)) {
41
*message = message->substr(::strlen(kGlobalWarningMessage));
42
return MessageType::GlobalWarning;
43
}
44
return MessageType::Unknown;
45
}
46
47
// Deduces a location specification from the given message. A location
48
// specification is of the form "<source-name>:<line-number>:" and a trailing
49
// space. If the deduction is successful, returns true and updates source_name
50
// and line_number to the deduced source name and line numer respectively. The
51
// prefix standing for the location specification in message is skipped.
52
// Otherwise, returns false and keeps all parameters untouched.
53
bool DeduceLocationSpec(string_piece* message, string_piece* source_name,
54
string_piece* line_number) {
55
if (!message || message->empty()) {
56
return false;
57
}
58
59
// When we find a pattern like this:
60
// colon
61
// digits
62
// colon
63
// space
64
// Then deduce that the source_name is the text before the first colon,
65
// the line number is the digits, and the message is the text after the
66
// second colon.
67
68
const size_t size = message->size();
69
if (size <= 4) {
70
// A valid message must have a colon, a digit, a colon, and a space.
71
return false;
72
}
73
// The last possible position of the first colon.
74
const size_t first_colon_cutoff = size - 4;
75
// The last possible position of the second colon.
76
const size_t next_colon_cutoff = size - 2;
77
78
for (size_t first_colon_pos = message->find_first_of(':'), next_colon_pos = 0;
79
80
// There is a first colon, and it's not too close to the end
81
(first_colon_pos != string_piece::npos) &&
82
(first_colon_pos <= first_colon_cutoff);
83
84
// Try the next pair of colons.
85
first_colon_pos = next_colon_pos) {
86
// We're guaranteed to have at least 3 more characters.
87
// Guarantee progress toward the end of the string.
88
next_colon_pos = message->find_first_of(':', first_colon_pos + 1);
89
if ((next_colon_pos == string_piece::npos) ||
90
(next_colon_pos > next_colon_cutoff)) {
91
// No valid solution.
92
return false;
93
}
94
if (first_colon_pos + 1 == next_colon_pos) {
95
// There is no room for digits.
96
continue;
97
}
98
if ((message->data()[next_colon_pos + 1] != ' ')) {
99
// There is no space character after the second colon.
100
continue;
101
}
102
if (message->find_first_not_of("0123456789", first_colon_pos + 1) ==
103
next_colon_pos) {
104
// We found the first solution.
105
*source_name = message->substr(0, first_colon_pos);
106
*line_number = message->substr(first_colon_pos + 1,
107
next_colon_pos - 1 - first_colon_pos);
108
*message = message->substr(next_colon_pos + 2);
109
return true;
110
}
111
}
112
113
return false;
114
}
115
116
// Returns true if the given message is a summary message.
117
bool IsSummaryMessage(const string_piece& message) {
118
const size_t space_loc = message.find_first_of(' ');
119
if (space_loc == string_piece::npos) return false;
120
const string_piece number = message.substr(0, space_loc);
121
const string_piece rest = message.substr(space_loc + 1);
122
if (!std::all_of(number.begin(), number.end(), ::isdigit)) return false;
123
if (!rest.starts_with("compilation errors.")) return false;
124
return true;
125
}
126
127
} // anonymous namespace
128
129
MessageType ParseGlslangOutput(const string_piece& message,
130
bool warnings_as_errors, bool suppress_warnings,
131
string_piece* source_name,
132
string_piece* line_number, string_piece* rest) {
133
string_piece rest_of_message(message);
134
source_name->clear();
135
line_number->clear();
136
rest->clear();
137
138
// The glslang warning/error messages are typically of the following form:
139
// <message-type> <location-specification> <message-body>
140
//
141
// <message-type> can be "WARNING:", "ERROR:", or "Warning, ". "WARNING:"
142
// means a warning message for a certain line, while "Warning, " means a
143
// global one.
144
//
145
// <location-specification> is of the form:
146
// <filename-or-string-number>:<line-number>:
147
// It doesn't exist if the warning/error message is a global one.
148
//
149
// See Glslang's TInfoSink class implementation for details.
150
151
bool is_error = false;
152
153
// Handle <message-type>.
154
switch (DeduceMessageType(&rest_of_message)) {
155
case MessageType::Warning:
156
if (suppress_warnings) return MessageType::Ignored;
157
break;
158
case MessageType::Error:
159
is_error = true;
160
break;
161
case MessageType::GlobalWarning:
162
if (suppress_warnings) return MessageType::Ignored;
163
*rest = rest_of_message;
164
return warnings_as_errors ? MessageType::GlobalError
165
: MessageType::GlobalWarning;
166
case MessageType::Unknown:
167
*rest = rest_of_message;
168
return MessageType::Unknown;
169
default:
170
break;
171
}
172
173
rest_of_message = rest_of_message.strip_whitespace();
174
if (rest_of_message.empty()) return MessageType::Unknown;
175
176
// Now we have stripped the <message-type>. Try to see if we can find
177
// a <location-specification>.
178
if (DeduceLocationSpec(&rest_of_message, source_name, line_number)) {
179
*rest = rest_of_message;
180
return (is_error || warnings_as_errors) ? MessageType::Error
181
: MessageType::Warning;
182
} else {
183
// No <location-specification>. This is a global warning/error message.
184
// A special kind of global message is summary message, which should
185
// start with a number.
186
*rest = rest_of_message;
187
if (IsSummaryMessage(rest_of_message)) {
188
return (is_error || warnings_as_errors) ? MessageType::ErrorSummary
189
: MessageType::WarningSummary;
190
}
191
return (is_error || warnings_as_errors) ? MessageType::GlobalError
192
: MessageType::GlobalWarning;
193
}
194
return MessageType::Unknown;
195
}
196
197
bool PrintFilteredErrors(const string_piece& file_name,
198
std::ostream* error_stream, bool warnings_as_errors,
199
bool suppress_warnings, const char* error_list,
200
size_t* total_warnings, size_t* total_errors) {
201
const char* ignored_error_strings[] = {
202
"Warning, version 310 is not yet complete; most version-specific "
203
"features are present, but some are missing.",
204
"Warning, version 400 is not yet complete; most version-specific "
205
"features are present, but some are missing.",
206
"Warning, version 410 is not yet complete; most version-specific "
207
"features are present, but some are missing.",
208
"Warning, version 420 is not yet complete; most version-specific "
209
"features are present, but some are missing.",
210
"Warning, version 430 is not yet complete; most version-specific "
211
"features are present, but some are missing.",
212
"Warning, version 440 is not yet complete; most version-specific "
213
"features are present, but some are missing.",
214
"Warning, version 450 is not yet complete; most version-specific "
215
"features are present, but some are missing.",
216
"Linked vertex stage:", "Linked fragment stage:",
217
"Linked tessellation control stage:",
218
"Linked tessellation evaluation stage:", "Linked geometry stage:",
219
"Linked compute stage:", ""};
220
size_t existing_total_errors = *total_errors;
221
string_piece error_messages(error_list);
222
for (const string_piece& message : error_messages.get_fields('\n')) {
223
if (std::find(std::begin(ignored_error_strings),
224
std::end(ignored_error_strings),
225
message) == std::end(ignored_error_strings)) {
226
string_piece source_name;
227
string_piece line_number;
228
string_piece rest;
229
const MessageType type =
230
ParseGlslangOutput(message, warnings_as_errors, suppress_warnings,
231
&source_name, &line_number, &rest);
232
string_piece name = file_name;
233
if (!source_name.empty()) {
234
// -1 is the string number for the preamble injected by us.
235
name = source_name == "-1" ? "<command line>" : source_name;
236
}
237
switch (type) {
238
case MessageType::Error:
239
case MessageType::Warning:
240
assert(!name.empty() && !line_number.empty() && !rest.empty());
241
*error_stream << name << ":" << line_number << ": "
242
<< (type == MessageType::Error ? "error: "
243
: "warning: ")
244
<< rest.strip_whitespace() << std::endl;
245
*total_errors += type == MessageType::Error;
246
*total_warnings += type == MessageType::Warning;
247
break;
248
case MessageType::ErrorSummary:
249
case MessageType::WarningSummary:
250
break;
251
case MessageType::GlobalError:
252
case MessageType::GlobalWarning:
253
assert(!rest.empty());
254
*total_errors += type == MessageType::GlobalError;
255
*total_warnings += type == MessageType::GlobalWarning;
256
*error_stream << name << ": "
257
<< (type == MessageType::GlobalError ? "error"
258
: "warning")
259
<< ": " << rest.strip_whitespace() << std::endl;
260
break;
261
case MessageType::Unknown:
262
*error_stream << name << ":";
263
*error_stream << " " << message << std::endl;
264
break;
265
case MessageType::Ignored:
266
break;
267
}
268
}
269
}
270
return (existing_total_errors == *total_errors);
271
}
272
273
// Outputs the number of warnings and errors if there are any.
274
void OutputMessages(std::ostream* error_stream, size_t total_warnings,
275
size_t total_errors) {
276
if (total_warnings > 0 || total_errors > 0) {
277
if (total_warnings > 0 && total_errors > 0) {
278
*error_stream << total_warnings << " warning"
279
<< (total_warnings > 1 ? "s" : "") << " and "
280
<< total_errors << " error" << (total_errors > 1 ? "s" : "")
281
<< " generated." << std::endl;
282
} else if (total_warnings > 0) {
283
*error_stream << total_warnings << " warning"
284
<< (total_warnings > 1 ? "s" : "") << " generated."
285
<< std::endl;
286
} else if (total_errors > 0) {
287
*error_stream << total_errors << " error" << (total_errors > 1 ? "s" : "")
288
<< " generated." << std::endl;
289
}
290
}
291
}
292
293
} // namespace glslc
294
295