1 /***************************************************************************
2 **
3 ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the utilities of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "legacyspecparser.h"
30
31 #include <QDebug>
32 #include <QFile>
33 #include <QRegExp>
34 #include <QStringList>
35 #include <QTextStream>
36
37 #ifdef SPECPARSER_DEBUG
38 #define qLegacySpecParserDebug qDebug
39 #else
40 #define qLegacySpecParserDebug QT_NO_QDEBUG_MACRO
41 #endif
42
parse()43 bool LegacySpecParser::parse()
44 {
45 // Get the mapping form generic types to specific types suitable for use in C-headers
46 if (!parseTypeMap())
47 return false;
48
49 // Open up a stream on the actual OpenGL function spec file
50 QFile file(specFileName());
51 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
52 qWarning() << "Failed to open spec file:" << specFileName() << "Aborting";
53 return false;
54 }
55
56 QTextStream stream(&file);
57
58 // Extract the info that we need
59 parseFunctions(stream);
60 return true;
61 }
62
parseTypeMap()63 bool LegacySpecParser::parseTypeMap()
64 {
65 QFile file(typeMapFileName());
66 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
67 qWarning() << "Failed to open type file:" << typeMapFileName() << "Aborting";
68 return false;
69 }
70
71 QTextStream stream(&file);
72
73 static QRegExp typeMapRegExp("([^,]+)\\W+([^,]+)");
74
75 while (!stream.atEnd()) {
76 QString line = stream.readLine();
77
78 if (line.startsWith(QLatin1Char('#')))
79 continue;
80
81 if (typeMapRegExp.indexIn(line) != -1) {
82 QString key = typeMapRegExp.cap(1).simplified();
83 QString value = typeMapRegExp.cap(2).simplified();
84
85 // Special case for void
86 if (value == QLatin1String("*"))
87 value = QStringLiteral("void");
88
89 m_typeMap.insert(key, value);
90 qLegacySpecParserDebug() << "Found type mapping from" << key << "=>" << value;
91 }
92 }
93
94 return true;
95 }
96
parseEnums()97 void LegacySpecParser::parseEnums()
98 {
99 }
100
parseFunctions(QTextStream & stream)101 void LegacySpecParser::parseFunctions(QTextStream &stream)
102 {
103 static QRegExp functionRegExp("^(\\w+)\\(.*\\)");
104 static QRegExp returnRegExp("^\\treturn\\s+(\\S+)");
105 static QRegExp argumentRegExp("param\\s+(\\S+)\\s+(\\S+) (\\S+) (\\S+)");
106 static QRegExp versionRegExp("^\\tversion\\s+(\\S+)");
107 static QRegExp deprecatedRegExp("^\\tdeprecated\\s+(\\S+)");
108 static QRegExp categoryRegExp("^\\tcategory\\s+(\\S+)");
109 static QRegExp categoryVersionRegExp("VERSION_(\\d)_(\\d)");
110 static QRegExp extToCoreVersionRegExp("passthru:\\s/\\*\\sOpenGL\\s(\\d)\\.(\\d)\\s.*\\sextensions:");
111 static QRegExp extToCoreRegExp("passthru:\\s/\\*\\s(ARB_\\S*)\\s.*\\*/");
112
113 Function currentFunction;
114 VersionProfile currentVersionProfile;
115 QString currentCategory;
116 bool haveVersionInfo = false;
117 bool acceptCurrentFunctionInCore = false;
118 bool acceptCurrentFunctionInExtension = false;
119
120 QHash<QString, Version> extensionsNowInCore;
121 Version extToCoreCurrentVersion;
122 int functionCount = 0;
123
124 QSet<Version> versions;
125
126 while (!stream.atEnd()) {
127 QString line = stream.readLine();
128 if (line.startsWith("#"))
129 continue;
130
131 if (functionRegExp.indexIn(line) != -1) {
132
133 if (!currentFunction.name.isEmpty()) {
134
135 // NB - Special handling!
136 // Versions 4.2 and 4.3 (and probably newer) add functionality by
137 // subsuming extensions such as ARB_texture_storage. However, some extensions
138 // also include functions to interact with the EXT_direct_state_access
139 // extension. These functions should be added to the DSA extension rather
140 // than the core functionality. The core will already contain non-DSA
141 // versions of these functions.
142 if (acceptCurrentFunctionInCore && currentFunction.name.endsWith(QLatin1String("EXT"))) {
143 acceptCurrentFunctionInCore = false;
144 acceptCurrentFunctionInExtension = true;
145 currentCategory = QStringLiteral("EXT_direct_state_access");
146 }
147
148 // Finish off previous function (if any) by inserting it into the core
149 // functionality or extension functionality (or both)
150 if (acceptCurrentFunctionInCore) {
151 m_functions.insert(currentVersionProfile, currentFunction);
152 versions.insert(currentVersionProfile.version);
153 }
154
155 if (acceptCurrentFunctionInExtension) {
156 FunctionProfile fp;
157 fp.profile = currentVersionProfile.profile;
158 fp.function = currentFunction;
159 m_extensionFunctions.insert(currentCategory, fp);
160 }
161 }
162
163 // Start a new function
164 ++functionCount;
165 haveVersionInfo = false;
166 acceptCurrentFunctionInCore = true;
167 acceptCurrentFunctionInExtension = false;
168 currentCategory = QString();
169 currentFunction = Function();
170
171 // We assume a core function unless we find a deprecated flag (see below)
172 currentVersionProfile = VersionProfile();
173 currentVersionProfile.profile = VersionProfile::CoreProfile;
174
175 // Extract the function name
176 QString functionName = functionRegExp.cap(1);
177 currentFunction.name = functionName;
178 qLegacySpecParserDebug() << "Found function:" << functionName;
179
180 } else if (argumentRegExp.indexIn(line) != -1) {
181 // Extract info about this function argument
182 Argument arg;
183 arg.name = argumentRegExp.cap(1);
184
185 QString type = argumentRegExp.cap(2); // Lookup in type map
186 arg.type = m_typeMap.value(type);
187
188 QString direction = argumentRegExp.cap(3);
189 if (direction == QLatin1String("in")) {
190 arg.direction = Argument::In;
191 } else if (direction == QLatin1String("out")) {
192 arg.direction = Argument::Out;
193 } else {
194 qWarning() << "Invalid argument direction found:" << direction;
195 acceptCurrentFunctionInCore = false;
196 }
197
198 QString mode = argumentRegExp.cap(4);
199 if (mode == QLatin1String("value")) {
200 arg.mode = Argument::Value;
201 } else if (mode == QLatin1String("array")) {
202 arg.mode = Argument::Array;
203 } else if (mode == QLatin1String("reference")) {
204 arg.mode = Argument::Reference;
205 } else {
206 qWarning() << "Invalid argument mode found:" << mode;
207 acceptCurrentFunctionInCore = false;
208 }
209
210 qLegacySpecParserDebug() << " argument:" << arg.type << arg.name;
211 currentFunction.arguments.append(arg);
212
213 } else if (returnRegExp.indexIn(line) != -1) {
214 // Lookup the return type from the typemap
215 QString returnTypeKey = returnRegExp.cap(1).simplified();
216 if (!m_typeMap.contains(returnTypeKey)) {
217 qWarning() << "Unknown return type found:" << returnTypeKey;
218 acceptCurrentFunctionInCore = false;
219 }
220 QString returnType = m_typeMap.value(returnTypeKey);
221 qLegacySpecParserDebug() << " return type:" << returnType;
222 currentFunction.returnType = returnType;
223
224 } else if (versionRegExp.indexIn(line) != -1 && !haveVersionInfo) { // Only use version line if no other source
225 // Extract the OpenGL version in which this function was introduced
226 QString version = versionRegExp.cap(1);
227 qLegacySpecParserDebug() << " version:" << version;
228 QStringList parts = version.split(QLatin1Char('.'));
229 if (parts.size() != 2) {
230 qWarning() << "Found invalid version number";
231 continue;
232 }
233 int majorVersion = parts.first().toInt();
234 int minorVersion = parts.last().toInt();
235 Version v;
236 v.major = majorVersion;
237 v.minor = minorVersion;
238 currentVersionProfile.version = v;
239
240 } else if (deprecatedRegExp.indexIn(line) != -1) {
241 // Extract the OpenGL version in which this function was deprecated.
242 // If it is OpenGL 3.1 then it must be a compatibility profile function
243 QString deprecatedVersion = deprecatedRegExp.cap(1).simplified();
244 if (deprecatedVersion == QLatin1String("3.1") && !inDeprecationException(currentFunction.name))
245 currentVersionProfile.profile = VersionProfile::CompatibilityProfile;
246
247 } else if (categoryRegExp.indexIn(line) != -1) {
248 // Extract the category for this function
249 QString category = categoryRegExp.cap(1).simplified();
250 qLegacySpecParserDebug() << " category:" << category;
251
252 if (categoryVersionRegExp.indexIn(category) != -1) {
253 // Use the version info in the category in preference to the version
254 // entry as this is more applicable and consistent
255 int majorVersion = categoryVersionRegExp.cap(1).toInt();
256 int minorVersion = categoryVersionRegExp.cap(2).toInt();
257
258 Version v;
259 v.major = majorVersion;
260 v.minor = minorVersion;
261 currentVersionProfile.version = v;
262 haveVersionInfo = true;
263
264 } else {
265 // Make a note of the extension name and tag this function as being part of an extension
266 qLegacySpecParserDebug() << "Found category =" << category;
267 currentCategory = category;
268 acceptCurrentFunctionInExtension = true;
269
270 // See if this category (extension) is in our set of extensions that
271 // have now been folded into the core feature set
272 if (extensionsNowInCore.contains(category)) {
273 currentVersionProfile.version = extensionsNowInCore.value(category);
274 haveVersionInfo = true;
275 } else {
276 acceptCurrentFunctionInCore = false;
277 }
278 }
279
280 } else if (extToCoreVersionRegExp.indexIn(line) != -1) {
281 qLegacySpecParserDebug() << line;
282 int majorVersion = extToCoreVersionRegExp.cap(1).toInt();
283 int minorVersion = extToCoreVersionRegExp.cap(2).toInt();
284 extToCoreCurrentVersion.major = majorVersion;
285 extToCoreCurrentVersion.minor = minorVersion;
286
287 } else if (extToCoreRegExp.indexIn(line) != -1) {
288 QString extension = extToCoreRegExp.cap(1);
289 extensionsNowInCore.insert(extension, extToCoreCurrentVersion);
290 }
291 }
292
293 m_versions = versions.toList();
294 std::sort(m_versions.begin(), m_versions.end());
295 }
296
inDeprecationException(const QString & functionName) const297 bool LegacySpecParser::inDeprecationException(const QString &functionName) const
298 {
299 return functionName == QLatin1String("TexImage3D");
300 }
301