int XmlToKross::start() { inclass=0; while (!xml.atEnd()) { QXmlStreamReader::TokenType t=xml.readNext(); QString str; switch(t) { case QXmlStreamReader::Invalid: qDebug() << "invalid token!" << xml.errorString() << endl; break; case QXmlStreamReader::StartDocument: writeDocument(); break; case QXmlStreamReader::StartElement: // qDebug() << "Element " << xml.name().toString() << endl; str=xml.name().toString(); if(str=="Class") { inclass++; if(inclass==1) writeClass(xml.attributes().value(QString(), "name").toString(), QString(), QList<QStringList>()); } else if(str=="Function") { QString funcname=xml.attributes().value(QString(), "name").toString(); QString rettype=xml.attributes().value(QString(), "type_name").toString(); bool isConst=xml.attributes().value(QString(), "constant").toString()==QChar('1'); bool isVirtual=xml.attributes().value(QString(), "virtual").toString()==QChar('1'); currentMethod=method(); currentMethod.access=xml.attributes().value(QString(), "access").toString(); currentMethod.funcname=funcname; currentMethod.returnType=rettype; currentMethod.isConst=isConst; currentMethod.isVirtual=isVirtual; if(!definedClasses.isEmpty() && ( funcname==definedClasses.last() || funcname=='~'+definedClasses.last() ) ) currentMethod.access="constructor"; } else if(str=="Argument") { method::argument arg; arg.name=xml.attributes().value(QString(), "name").toString(); arg.type=xml.attributes().value(QString(), "type_name").toString(); arg.def=xml.attributes().value(QString(), "defaultvalue").toString(); QString context=xml.attributes().value(QString(), "context").toString(); if(!arg.def.isEmpty() && arg.type.startsWith(context)) arg.def.prepend(context+"::"); currentMethod.args.append(arg); } else if(str=="Namespace") { QString name=xml.attributes().value(QString(), "name").toString(); qDebug() << "entering to namespace " << name << inNamespace; if(inNamespace!=name) { if(!inNamespace.isEmpty()) inNamespace.append("::"); inNamespace.append(name); writeNamespace(name); qDebug() << "current status" << inNamespace; } } else if(str=="Variable" && inclass==1 && xml.attributes().value(QString(), "access").toString()=="public") { writeVariable(xml.attributes().value(QString(), "name").toString(), xml.attributes().value(QString(), "type_name").toString(), xml.attributes().value(QString(), "type_constant").toString()=="1"); } else if(str=="Enum") { flags.clear(); flags.append(xml.attributes().value(QString(), "name").toString()); } else if(str=="Enumerator") flags.append(xml.attributes().value(QString(), "name").toString()); break; case QXmlStreamReader::EndDocument: writeEndDocument(); break; case QXmlStreamReader::EndElement: str=xml.name().toString(); if(str=="Class") { if(inclass==1) writeEndClass(); inclass--; } else if(str=="Function" && currentMethod.access=="public" && inclass==1) writeEndFunction(currentMethod); else if(str=="Enum") writeEndEnum(flags); else if(str=="Namespace") { inNamespace.resize(inNamespace.lastIndexOf("::")); } break; default: break; } } if (xml.error()) { qDebug() << "error!" << xml.errorString(); return 2; } return 0; }
int main(int argc, char** argv) { if (argc != 6) { std::cout << "This program takes in input glad.h and outputs the include and implementation files for OSMesa OpenGL function and regular OpenGL functions." << std::endl; std::cout << "Usage: generateGLIncludes <glad.h path> <output dir path> <namespace name> <baseFileName> <inlcude glad debug symbols>" << std::endl; std::cout << "Example: generateGLIncludes /Users/alexandre/development/Natron/Global/gladRel/include/glad/glad.h /Users/alexandre/development/Natron/Engine Natron OSGLFunctions 1" << std::endl; return 1; } QFile f(argv[1]); if ( !f.open(QIODevice::ReadOnly) ) { std::cout << "Could not open " << argv[1] << std::endl; return 1; } // Check that output path exists QDir outputDir(argv[2]); if ( !outputDir.exists() ) { std::cout << argv[2] << " does not seem to be a valid directory" << std::endl; return 1; } QString namespaceName(argv[3]); QString baseFilename(argv[4]); bool supportGladDebug; { QString supportDebugSymbolsStr(argv[5]); supportGladDebug = (bool)supportDebugSymbolsStr.toInt(); } QString absoluteDirPath = outputDir.absolutePath(); QString outputHeaderFilename = absoluteDirPath + "/" + baseFilename + ".h"; QFile of_header(outputHeaderFilename); if ( !of_header.open(QIODevice::WriteOnly) ) { std::cout << "Could not open " << outputHeaderFilename.toStdString() << std::endl; return 1; } QTextStream ots_header(&of_header); QTextStream its(&f); QString definesStr; std::list<FunctionSignature> signatures; QString functionTypedefsStr; QString prevLine; while ( !its.atEnd() ) { // Read each line of glad.h QString line = its.readLine(); { // Search for a define QString toFind = "#define GL_"; int found = line.indexOf(toFind); if (found != -1) { definesStr += line; definesStr += "\n"; } } // Search for a function QString typedefToken("typedef "); QString pfnToken("(APIENTRYP PFNGL"); int foundFuncDef = line.indexOf(typedefToken); int foundPNFToken = line.indexOf(pfnToken); if ( (foundFuncDef != -1) && (foundPNFToken != -1) ) { int pos = foundPNFToken + pfnToken.size(); int foundFirstEndParenthesis = line.indexOf(')', pos); assert(foundFirstEndParenthesis != -1); FunctionSignature signature; QString lastFuncNameCaps = line.mid(pos, foundFirstEndParenthesis - pos); signature.signature = line.mid(foundFirstEndParenthesis); // "near" and "far" are defined as macros in windows.h signature.signature.replace("GLdouble near, GLdouble far", "GLdouble nearVal, GLdouble farVal"); signature.returnType = line.mid( foundFuncDef + typedefToken.size(), foundPNFToken - 1 - ( foundFuncDef + typedefToken.size() ) ); QString funcTypeDefStr = "typedef "; funcTypeDefStr += signature.returnType; funcTypeDefStr += " (*PFNGL"; funcTypeDefStr += lastFuncNameCaps; funcTypeDefStr += signature.signature; funcTypeDefStr += "\n"; functionTypedefsStr += funcTypeDefStr; // Remove the extraneous ; at the end of the signature // Also remove the prepending ) signature.signature.remove(0, 1); signature.signature.remove(signature.signature.size() - 1, 1); signature.funcPNType = "PFNGL"; signature.funcPNType += lastFuncNameCaps; // extract parameters { int i = 1; // start after the leading ( while ( i < signature.signature.size() ) { QString param; while ( signature.signature[i] != QChar(',') && signature.signature[i] != QChar(')') ) { param.append(signature.signature[i]); ++i; } // Now only keep the name of the parameter { int j = param.size() - 1; while ( j >= 0 && param[j].isLetterOrNumber() ) { --j; } param = param.mid(j + 1); } signature.parameters.append(param); assert( signature.signature[i] == QChar(',') || signature.signature[i] == QChar(')') ); ++i; // bypass last character if ( signature.signature[i] == QChar(')') ) { break; } } } // we caught a function typedef before, we expect to read the following #define glXxxx function with the appropriate case // in release glad.h, the next line is of the type GLAPI PFNGLCOLOR4FVPROC glad_glColor4fv; // the line after that is the one we want #define glColor4fv glad_glColor4fv line = its.readLine(); assert( !its.atEnd() ); line = its.readLine(); QString toFind("#define gl"); int foundDefine = line.indexOf(toFind); if (foundDefine == -1) { std::cout << "Parser failed to find #define glXXX statement 2 lines after a function typedef, make sure that you are running this program against a release version of glad.h" << std::endl; return 1; } // Check that this is the same symbol // Remove the PROC at the end of the func def lastFuncNameCaps.remove("PROC"); int checkIndex = toFind.size(); QString symbolStart = line.mid(checkIndex); assert( symbolStart.startsWith(lastFuncNameCaps, Qt::CaseInsensitive) ); { int i = 8; // start on the g //extract the function name while ( i < line.size() && line.at(i) != QChar(' ') ) { signature.funcName.push_back( line.at(i) ); ++i; } } signatures.push_back(signature); } // if (foundFuncDef != -1 && foundPNFToken != -1) { prevLine = line; } writeHeader(ots_header); writePODs(ots_header); ots_header << definesStr << "\n" "\n"; ots_header << functionTypedefsStr << "\n" "\n"; writeStartClass(namespaceName, ots_header); // Define the singleton ots_header << " static OSGLFunctions<USEOPENGL>& getInstance()\n" " {\n" " static OSGLFunctions<USEOPENGL> instance;\n" "\n" " return instance;\n" " }\n" "\n" " // load function, implemented in _gl.h and _mesa.h\n" " void load_functions();\n" "\n" " // private constructor\n" " OSGLFunctions() { load_functions(); }\n" "\n"; // Declare member functions for (std::list<FunctionSignature>::iterator it = signatures.begin(); it != signatures.end(); ++it) { ots_header << " " << it->funcPNType << " _" << it->funcName << ";\n"; } ots_header << "\n"; ots_header << "public:\n" "\n"; ots_header << " // static non MT-safe load function that must be called once to initialize functions\n" " static void load()\n" " {\n" " (void)getInstance();\n" " }\n" "\n" " static bool isGPU()\n" " {\n" " return USEOPENGL;\n" " }\n"; for (std::list<FunctionSignature>::iterator it = signatures.begin(); it != signatures.end(); ++it) { QString lineStart = " static " + it->returnType + " " + it->funcName; QString indentedSig = it->signature; indentedSig.replace( ", ", ",\n" + QString(lineStart.size() + 1, ' ') ); ots_header << "\n" << lineStart << indentedSig << "\n" " {\n"; if (it->returnType == "void") { ots_header << " "; } else { ots_header << " return "; } ots_header << "getInstance()._" << it->funcName << "("; QStringList::const_iterator next = it->parameters.begin(); if ( !it->parameters.isEmpty() ) { ++next; } for (QStringList::const_iterator it2 = it->parameters.begin(); it2 != it->parameters.end(); ++it2) { ots_header << *it2; if ( next != it->parameters.end() ) { ots_header << ", "; ++next; } } ots_header << ");\n" " }\n"; } writeEndClass(namespaceName, ots_header); writeFooter(ots_header); writeImplementationCppFile(namespaceName, baseFilename, signatures, absoluteDirPath, "gl", true, supportGladDebug); writeImplementationCppFile(namespaceName, baseFilename, signatures, absoluteDirPath, "mesa", false, supportGladDebug); return 0; } // main