Introduce helper function check() to ease throwing on spec error detection. (#502)

* Introduce helper function check() to ease throwing on spec error detection.

* Use some more checks

* Some more checks
This commit is contained in:
Andreas Süßenbach 2020-02-04 10:35:33 +01:00 committed by GitHub
parent 4d53dac22b
commit cf39cd4f0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 228 additions and 162 deletions

View File

@ -29,9 +29,9 @@ void appendTypesafeStuff(std::string &str, std::string const& typesafeCheck);
void appendVersionCheck(std::string & str, std::string const& version); void appendVersionCheck(std::string & str, std::string const& version);
bool beginsWith(std::string const& text, std::string const& prefix); bool beginsWith(std::string const& text, std::string const& prefix);
bool endsWith(std::string const& text, std::string const& postfix); bool endsWith(std::string const& text, std::string const& postfix);
template <typename T> void checkAlias(std::map<std::string, T> const& data, std::string const& name, int line); void check(bool condition, int line, std::string const& message);
void checkAttributes(std::map<std::string, std::string> const& attributes, int line, std::map<std::string, std::set<std::string>> const& required, std::map<std::string, std::set<std::string>> const& optional); void checkAttributes(std::map<std::string, std::string> const& attributes, int line, std::map<std::string, std::set<std::string>> const& required, std::map<std::string, std::set<std::string>> const& optional);
void checkElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& values); void checkElements(int line, std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& required, std::set<std::string> const& optional = {});
void checkEmptyElement(tinyxml2::XMLElement const* element); void checkEmptyElement(tinyxml2::XMLElement const* element);
void checkOrderedElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::vector<std::string> const& values); void checkOrderedElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::vector<std::string> const& values);
void cleanup(std::stringstream & ss); void cleanup(std::stringstream & ss);
@ -62,6 +62,7 @@ std::string toUpperCase(std::string const& name);
std::vector<std::string> tokenize(std::string tokenString, char separator); std::vector<std::string> tokenize(std::string tokenString, char separator);
std::string trim(std::string const& input); std::string trim(std::string const& input);
std::string trimEnd(std::string const& input); std::string trimEnd(std::string const& input);
void warn(bool condition, int line, std::string const& message);
const std::set<std::string> nonConstSTypeStructs = { "VkBaseInStructure", "VkBaseOutStructure" }; const std::set<std::string> nonConstSTypeStructs = { "VkBaseInStructure", "VkBaseOutStructure" };
@ -159,12 +160,11 @@ bool endsWith(std::string const& text, std::string const& postfix)
return !postfix.empty() && (postfix.length() <= text.length()) && (text.substr(text.length() - postfix.length()) == postfix); return !postfix.empty() && (postfix.length() <= text.length()) && (text.substr(text.length() - postfix.length()) == postfix);
} }
template <typename T> void check(bool condition, int line, std::string const& message)
void checkAlias(std::map<std::string, T> const& data, std::string const& name, int line)
{ {
if (data.find(name) == data.end()) if (!condition)
{ {
throw std::runtime_error("Spec error on line " + std::to_string(line) + ": missing alias <" + name + ">"); throw std::runtime_error("Spec error on line " + std::to_string(line) + ": " + message);
} }
} }
@ -179,14 +179,8 @@ void checkAttributes(std::map<std::string, std::string> const& attributes, int l
for (auto const& r : required) for (auto const& r : required)
{ {
auto attributesIt = attributes.find(r.first); auto attributesIt = attributes.find(r.first);
if (attributesIt == attributes.end()) check(attributesIt != attributes.end(), line, "missing attribute <" + r.first + ">");
{ check(r.second.empty() || (r.second.find(attributesIt->second) != r.second.end()), line, "unexpected attribute value <" + attributesIt->second + "> in attribute <" + r.first + ">");
throw std::runtime_error("Spec error on line " + std::to_string(line) + ": missing attribute <" + r.first + ">");
}
if (!r.second.empty() && (r.second.find(attributesIt->second) == r.second.end()))
{
throw std::runtime_error("Spec error on line " + std::to_string(line) + ": unexpected attribute value <" + attributesIt->second + "> in attribute <" + r.first + ">");
}
} }
// check if all not required attributes or optional, and if there is a set of allowed values, check if the actual value is part of that set // check if all not required attributes or optional, and if there is a set of allowed values, check if the actual value is part of that set
for (auto const& a : attributes) for (auto const& a : attributes)
@ -196,7 +190,7 @@ void checkAttributes(std::map<std::string, std::string> const& attributes, int l
auto optionalIt = optional.find(a.first); auto optionalIt = optional.find(a.first);
if (optionalIt == optional.end()) if (optionalIt == optional.end())
{ {
std::cerr << "warning: " << "Unknown attribute " + a.first + " in line " + std::to_string(line) + "!" << std::endl; warn(false, line, "unknown attribute <" + a.first + ">");
continue; continue;
} }
if (!optionalIt->second.empty()) if (!optionalIt->second.empty())
@ -204,45 +198,41 @@ void checkAttributes(std::map<std::string, std::string> const& attributes, int l
std::vector<std::string> values = tokenize(a.second, ','); std::vector<std::string> values = tokenize(a.second, ',');
for (auto const& v : values) for (auto const& v : values)
{ {
if (optionalIt->second.find(v) == optionalIt->second.end()) check(optionalIt->second.find(v) != optionalIt->second.end(), line, "unexpected attribute value <" + v + "> in attribute <" + a.first + ">");
{
throw std::runtime_error("Spec error on line " + std::to_string(line) + ": unexpected attribute value <" + v + "> in attribute <" + a.first + ">");
}
} }
} }
} }
} }
} }
void checkElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& values) void checkElements(int line, std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& required, std::set<std::string> const& optional)
{ {
for (auto e : elements) std::set<std::string> encountered;
for (auto const& e : elements)
{ {
if (values.find(e->Value()) == values.end()) std::string value = e->Value();
{ encountered.insert(value);
std::cerr << "warning: Unknown element in spec on line: " << std::to_string(e->GetLineNum()) << " " << e->Value() << "!" << std::endl; check((required.find(value) != required.end()) || (optional.find(value) != optional.end()), e->GetLineNum(), "unknown element <" + value + ">");
} }
for (auto const& r : required)
{
check(encountered.find(r) != encountered.end(), line, "missing required element <" + r + ">");
} }
} }
void checkEmptyElement(tinyxml2::XMLElement const* element) void checkEmptyElement(tinyxml2::XMLElement const* element)
{ {
checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); int line = element->GetLineNum();
checkElements(getChildElements(element), {}); checkAttributes(getAttributes(element), line, {}, {});
checkElements(line, getChildElements(element), {});
} }
void checkOrderedElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::vector<std::string> const& values) void checkOrderedElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::vector<std::string> const& values)
{ {
for (size_t i = 0; i < elements.size(); i++) for (size_t i = 0; i < elements.size(); i++)
{ {
if (values.size() <= i) check(i < values.size(), elements[i]->GetLineNum(), std::string("unexpected surplus element <") + elements[i]->Value() + ">");
{ check(values[i] == elements[i]->Value(), elements[i]->GetLineNum(), std::string("unexpected element <") + elements[i]->Value() + ">, expected <" + values[i] + ">");
throw std::runtime_error("Spec error on line " + std::to_string(elements[i]->GetLineNum()) + ": unexpected surplus element <" + elements[i]->Value() + ">");
}
if (values[i] != elements[i]->Value())
{
throw std::runtime_error("Spec error on line " + std::to_string(elements[i]->GetLineNum()) + ": unexpected element <" + elements[i]->Value() + ">, expected <" + values[i] + ">");
}
} }
} }
@ -691,6 +681,14 @@ std::string trimEnd(std::string const& input)
return result; return result;
} }
void warn(bool condition, int line, std::string const& message)
{
if (!condition)
{
std::cerr << "Spec warning on line " << std::to_string(line) << " " << message << "!" << std::endl;
}
}
VulkanHppGenerator::VulkanHppGenerator() VulkanHppGenerator::VulkanHppGenerator()
{ {
m_handles.insert(std::make_pair("", HandleData())); // insert the default "handle" without class (for createInstance, and such) m_handles.insert(std::make_pair("", HandleData())); // insert the default "handle" without class (for createInstance, and such)
@ -842,7 +840,7 @@ void VulkanHppGenerator::appendBaseTypes(std::string & str) const
{ {
if (baseType.first != "VkFlags") // filter out VkFlags if (baseType.first != "VkFlags") // filter out VkFlags
{ {
str += " using " + stripPrefix(baseType.first, "Vk") + " = " + baseType.second + ";\n"; str += " using " + stripPrefix(baseType.first, "Vk") + " = " + baseType.second.type + ";\n";
} }
} }
} }
@ -853,10 +851,7 @@ void VulkanHppGenerator::appendBitmasks(std::string & str) const
{ {
auto bitmaskBits = m_enums.find(bitmask.second.requirement); auto bitmaskBits = m_enums.find(bitmask.second.requirement);
bool hasBits = (bitmaskBits != m_enums.end()); bool hasBits = (bitmaskBits != m_enums.end());
if (!bitmask.second.requirement.empty() && !hasBits) check(bitmask.second.requirement.empty() || hasBits, bitmask.second.xmlLine, "bitmask <" + bitmask.first + "> references the undefined requirement <" + bitmask.second.requirement + ">");
{
throw std::runtime_error(bitmask.first + " references the undefined requirement '" + bitmask.second.requirement + "'");
}
std::string strippedBitmaskName = stripPrefix(bitmask.first, "Vk"); std::string strippedBitmaskName = stripPrefix(bitmask.first, "Vk");
std::string strippedEnumName = hasBits ? stripPrefix(bitmaskBits->first, "Vk") : ""; std::string strippedEnumName = hasBits ? stripPrefix(bitmaskBits->first, "Vk") : "";
@ -3008,17 +3003,15 @@ void VulkanHppGenerator::appendStructureChainValidation(std::string & str)
if (itExtend == m_structures.end()) if (itExtend == m_structures.end())
{ {
std::string errorString; std::string errorString;
errorString = extendName + " does not specify a struct in structextends field."; errorString = "<" + extendName + "> does not specify a struct in structextends field.";
// check if symbol name is an alias to a struct // check if symbol name is an alias to a struct
auto itAlias = std::find_if(m_structures.begin(), m_structures.end(), [&extendName](std::pair<std::string, StructureData> const &it) -> bool {return std::find(it.second.aliases.begin(), it.second.aliases.end(), extendName) != it.second.aliases.end(); }); auto itAlias = std::find_if(m_structures.begin(), m_structures.end(), [&extendName](std::pair<std::string, StructureData> const &it) -> bool {return std::find(it.second.aliases.begin(), it.second.aliases.end(), extendName) != it.second.aliases.end(); });
if (itAlias != m_structures.end()) if (itAlias != m_structures.end())
{ {
errorString += " The symbol is an alias and maps to " + itAlias->first + "."; errorString += " The symbol is an alias and maps to <" + itAlias->first + ">.";
} }
check(true, structure.second.xmlLine, errorString);
errorString += "\n";
throw std::runtime_error(errorString);
} }
if (structure.second.platform != itExtend->second.platform) if (structure.second.platform != itExtend->second.platform)
{ {
@ -3241,15 +3234,17 @@ bool VulkanHppGenerator::containsUnion(std::string const& type) const
void VulkanHppGenerator::checkCorrectness() void VulkanHppGenerator::checkCorrectness()
{ {
check(!m_vulkanLicenseHeader.empty(), -1, "missing license header");
for (auto const& baseType : m_baseTypes)
{
check(m_types.find(baseType.second.type) != m_types.end(), baseType.second.xmlLine, "basetype type <" + baseType.second.type + "> not specified");
}
for (auto const& handle : m_handles) for (auto const& handle : m_handles)
{ {
for (auto const& command: handle.second.commands) for (auto const& command: handle.second.commands)
{ {
// check that functions returning a VkResult specify successcodes // check that functions returning a VkResult specify successcodes
if ((command.second.returnType == "VkResult") && command.second.successCodes.empty()) check((command.second.returnType != "VkResult") || !command.second.successCodes.empty(), command.second.xmlLine, "missing successcodes on command <" + command.first + "> returning VkResult!");
{
throw std::runtime_error("Spec error on command Vk" + startUpperCase(command.first) + " : missing successcodes on command returning VkResult!");
}
} }
} }
} }
@ -3456,7 +3451,7 @@ void VulkanHppGenerator::linkCommandToHandle(std::string const& name, CommandDat
// put the command into the handle's list of commands // put the command into the handle's list of commands
assert(handleIt->second.commands.find(name) == handleIt->second.commands.end()); assert(handleIt->second.commands.find(name) == handleIt->second.commands.end());
handleIt->second.commands[name] = commandData; handleIt->second.commands.insert(std::make_pair(name, commandData));
// and store the handle in the command-to-handle map // and store the handle in the command-to-handle map
assert(m_commandToHandle.find(name) == m_commandToHandle.end()); assert(m_commandToHandle.find(name) == m_commandToHandle.end());
@ -3465,20 +3460,29 @@ void VulkanHppGenerator::linkCommandToHandle(std::string const& name, CommandDat
void VulkanHppGenerator::readBaseType(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readBaseType(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes)
{ {
checkAttributes(attributes, element->GetLineNum(), { { "category",{ "basetype" } } }, {}); int line = element->GetLineNum();
checkAttributes(attributes, line, { { "category",{ "basetype" } } }, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkOrderedElements(children, { "type", "name" }); checkElements(line, children, { "name", "type" });
checkEmptyElement(children[0]);
checkEmptyElement(children[1]);
std::string type = children[0]->GetText(); std::string name, type;
std::string name = children[1]->GetText(); for (auto child : children)
#if !defined(NDEBUG) {
assert((type == "uint32_t") || (type == "uint64_t")); std::string value = child->Value();
#endif if (value == "name")
{
assert(m_baseTypes.find(name) == m_baseTypes.end()); name = child->GetText();
m_baseTypes[name] = type; check(!name.empty(), child->GetLineNum(), "basetype name is empty");
}
else
{
assert(value == "type");
type = child->GetText();
check(!type.empty(), child->GetLineNum(), "basetype type is empty");
}
}
assert(!name.empty() && !type.empty());
check(m_baseTypes.insert(std::make_pair(name, BaseTypeData(type, element->GetLineNum()))).second, line, "basetype <" + name + "> already specified");
} }
void VulkanHppGenerator::readBitmask(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readBitmask(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes)
@ -3508,16 +3512,16 @@ void VulkanHppGenerator::readBitmask(tinyxml2::XMLElement const* element, std::m
requirement = requiresIt->second; requirement = requiresIt->second;
} }
m_bitmasks.insert(std::make_pair(name, BitmaskData(requirement))); m_bitmasks.insert(std::make_pair(name, BitmaskData(requirement, element->GetLineNum())));
} }
} }
void VulkanHppGenerator::readBitmaskAlias(int lineNum, std::string const& alias, std::map<std::string, std::string> const& attributes, std::vector<tinyxml2::XMLElement const*> const& children) void VulkanHppGenerator::readBitmaskAlias(int lineNum, std::string const& alias, std::map<std::string, std::string> const& attributes, std::vector<tinyxml2::XMLElement const*> const& children)
{ {
checkAttributes(attributes, lineNum, { { "alias",{} },{ "category",{ "bitmask" } },{ "name",{} } }, {}); // re-check on alias type! checkAttributes(attributes, lineNum, { { "alias",{} },{ "category",{ "bitmask" } },{ "name",{} } }, {}); // re-check on alias type!
checkElements(children, {}); checkElements(lineNum, children, {});
checkAlias(m_bitmasks, alias, lineNum); check(m_bitmasks.find(alias) != m_bitmasks.end(), lineNum, "missing alias <" + alias + ">.");
auto bitmasksIt = m_bitmasks.find(alias); auto bitmasksIt = m_bitmasks.find(alias);
assert((bitmasksIt != m_bitmasks.end()) && bitmasksIt->second.alias.empty()); assert((bitmasksIt != m_bitmasks.end()) && bitmasksIt->second.alias.empty());
@ -3542,7 +3546,7 @@ void VulkanHppGenerator::readCommand(tinyxml2::XMLElement const* element)
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
std::string name; std::string name;
CommandData commandData; CommandData commandData(element->GetLineNum());
auto aliasIt = attributes.find("alias"); auto aliasIt = attributes.find("alias");
if (aliasIt != attributes.end()) if (aliasIt != attributes.end())
{ {
@ -3550,7 +3554,7 @@ void VulkanHppGenerator::readCommand(tinyxml2::XMLElement const* element)
} }
else else
{ {
checkElements(children, { "implicitexternsyncparams", "param", "proto" }); checkElements(element->GetLineNum(), children, { "param", "proto" }, { "implicitexternsyncparams" });
commandData.successCodes = readCommandSuccessCodes(attributes); commandData.successCodes = readCommandSuccessCodes(attributes);
@ -3576,10 +3580,10 @@ void VulkanHppGenerator::readCommandAlias(int lineNum, std::string const& alias,
{ {
// for command aliases, create a copy of the aliased command // for command aliases, create a copy of the aliased command
checkAttributes(attributes, lineNum, { { "alias",{} },{ "name",{} } }, {}); // re-check on alias type! checkAttributes(attributes, lineNum, { { "alias",{} },{ "name",{} } }, {}); // re-check on alias type!
checkElements(children, {}); checkElements(lineNum, children, {});
std::string name = attributes.find("name")->second; std::string name = attributes.find("name")->second;
checkAlias(m_commandToHandle, alias, lineNum); check(m_commandToHandle.find(alias) != m_commandToHandle.end(), lineNum, "missing alias <" + alias + ">.");
auto handleIt = m_handles.find(m_commandToHandle.find(alias)->second); auto handleIt = m_handles.find(m_commandToHandle.find(alias)->second);
assert(handleIt != m_handles.end()); assert(handleIt != m_handles.end());
auto commandsIt = handleIt->second.commands.find(alias); auto commandsIt = handleIt->second.commands.find(alias);
@ -3596,7 +3600,7 @@ VulkanHppGenerator::ParamData VulkanHppGenerator::readCommandParam(tinyxml2::XML
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "externsync",{} },{ "len",{} },{ "noautovalidity",{ "true" } },{ "optional",{ "false", "true" } } }); checkAttributes(attributes, element->GetLineNum(), {}, { { "externsync",{} },{ "len",{} },{ "noautovalidity",{ "true" } },{ "optional",{ "false", "true" } } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "name", "type" }); checkElements(element->GetLineNum(), children, { "name", "type" });
ParamData paramData; ParamData paramData;
for (auto attribute : attributes) for (auto attribute : attributes)
@ -3652,7 +3656,7 @@ void VulkanHppGenerator::readCommands(tinyxml2::XMLElement const* element)
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } }); checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "command" }); checkElements(element->GetLineNum(), children, { "command" });
for (auto child : children) for (auto child : children)
{ {
@ -3680,14 +3684,15 @@ std::vector<std::string> VulkanHppGenerator::readCommandSuccessCodes(std::map<st
void VulkanHppGenerator::readComment(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readComment(tinyxml2::XMLElement const* element)
{ {
checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); int line = element->GetLineNum();
checkElements(getChildElements(element), {}); checkAttributes(getAttributes(element), line, {}, {});
checkElements(line, getChildElements(element), {});
assert(element->GetText()); assert(element->GetText());
std::string text = element->GetText(); std::string text = element->GetText();
if (text.find("\nCopyright") == 0) if (text.find("\nCopyright") == 0)
{ {
assert(m_vulkanLicenseHeader.empty()); check(m_vulkanLicenseHeader.empty(), line, "second encounter of a Copyright comment");
m_vulkanLicenseHeader = text; m_vulkanLicenseHeader = text;
// replace any '\n' with "\n// " // replace any '\n' with "\n// "
@ -3704,39 +3709,43 @@ void VulkanHppGenerator::readComment(tinyxml2::XMLElement const* element)
void VulkanHppGenerator::readDefine(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readDefine(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes)
{ {
checkAttributes(attributes, element->GetLineNum(), { { "category",{ "define" } } }, { { "name",{} } }); int line = element->GetLineNum();
checkAttributes(attributes, line, { { "category",{ "define" } } }, { { "name",{} } });
auto nameIt = attributes.find("name"); auto nameIt = attributes.find("name");
if (nameIt != attributes.end()) if (nameIt != attributes.end())
{ {
assert(!element->FirstChildElement()); check(!element->FirstChildElement(), line, "unknown formatting of type category=define name <" + nameIt->second + ">");
assert(nameIt->second == "VK_DEFINE_NON_DISPATCHABLE_HANDLE"); check(nameIt->second == "VK_DEFINE_NON_DISPATCHABLE_HANDLE", line, "unknown type category=define name <" + nameIt->second + ">");
check(element->LastChild() && element->LastChild()->ToText() && element->LastChild()->ToText()->Value(), line, "unknown formatting of type category=define named <" + nameIt->second + ">");
// filter out the check for the different types of VK_DEFINE_NON_DISPATCHABLE_HANDLE // filter out the check for the different types of VK_DEFINE_NON_DISPATCHABLE_HANDLE
std::string text = element->LastChild()->ToText()->Value(); std::string text = element->LastChild()->ToText()->Value();
size_t start = text.find("#if defined(__LP64__)"); size_t start = text.find("#if defined(__LP64__)");
check(start != std::string::npos, line, "unexpected text in type category=define named <" + nameIt->second + ">");
size_t end = text.find_first_of("\r\n", start + 1); size_t end = text.find_first_of("\r\n", start + 1);
check(end != std::string::npos, line, "unexpected text in type category=define named <" + nameIt->second + ">");
m_typesafeCheck = text.substr(start, end - start); m_typesafeCheck = text.substr(start, end - start);
} }
else if (element->GetText() && (trim(element->GetText()) == "struct")) else if (element->GetText() && (trim(element->GetText()) == "struct"))
{ {
#if !defined(NDEBUG) // here are a couple of structs as defines, which really are types!
tinyxml2::XMLElement const* child = element->FirstChildElement(); tinyxml2::XMLElement const* child = element->FirstChildElement();
assert(child && (strcmp(child->Value(), "name") == 0) && child->GetText()); check(child && (strcmp(child->Value(), "name") == 0) && child->GetText(), line, "unexpected formatting of type category=define");
m_defines.insert(child->GetText()); std::string text = child->GetText();
#endif check(m_types.insert(text).second, line, "type defined has already been speficied");
} }
else else
{ {
tinyxml2::XMLElement const* child = element->FirstChildElement(); tinyxml2::XMLElement const* child = element->FirstChildElement();
assert(child && !child->FirstAttribute() && (strcmp(child->Value(), "name") == 0) && child->GetText()); check(child && !child->FirstAttribute() && (strcmp(child->Value(), "name") == 0) && child->GetText(), line, "unknown formatting of type category define");
std::string text = trim(child->GetText()); std::string text = trim(child->GetText());
if (text == "VK_HEADER_VERSION") if (text == "VK_HEADER_VERSION")
{ {
m_version = trimEnd(element->LastChild()->ToText()->Value()); m_version = trimEnd(element->LastChild()->ToText()->Value());
} }
// ignore all the other defines // ignore all the other defines
assert(!child->NextSiblingElement() || (child->NextSiblingElement() && !child->NextSiblingElement()->FirstAttribute() && (strcmp(child->NextSiblingElement()->Value(), "type") == 0) && !child->NextSiblingElement()->NextSiblingElement())); warn(!child->NextSiblingElement() || (child->NextSiblingElement() && !child->NextSiblingElement()->FirstAttribute() && (strcmp(child->NextSiblingElement()->Value(), "type") == 0) && !child->NextSiblingElement()->NextSiblingElement()), line, "unknown formatting of type category define");
} }
} }
@ -3745,7 +3754,7 @@ void VulkanHppGenerator::readEnum(tinyxml2::XMLElement const* element, EnumData
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "alias",{} },{ "bitpos",{} },{ "comment",{} },{ "value",{} } }); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "alias",{} },{ "bitpos",{} },{ "comment",{} },{ "value",{} } });
assert((attributes.find("alias") != attributes.end()) + (attributes.find("bitpos") != attributes.end()) + (attributes.find("value") != attributes.end()) == 1); assert((attributes.find("alias") != attributes.end()) + (attributes.find("bitpos") != attributes.end()) + (attributes.find("value") != attributes.end()) == 1);
checkElements(getChildElements(element), {}); checkElements(element->GetLineNum(), getChildElements(element), {});
std::string name = attributes.find("name")->second; std::string name = attributes.find("name")->second;
std::string tag = findTag(m_tags, name, postfix); std::string tag = findTag(m_tags, name, postfix);
@ -3768,7 +3777,7 @@ void VulkanHppGenerator::readEnums(tinyxml2::XMLElement const* element)
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "comment",{} },{ "type",{ "bitmask", "enum" } } }); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "comment",{} },{ "type",{ "bitmask", "enum" } } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "comment", "enum", "unused" }); checkElements(element->GetLineNum(), children, {}, { "comment", "enum", "unused" });
std::string name = attributes.find("name")->second; std::string name = attributes.find("name")->second;
@ -3834,7 +3843,7 @@ void VulkanHppGenerator::readExtension(tinyxml2::XMLElement const* element)
{ "type",{ "device", "instance" } } { "type",{ "device", "instance" } }
}); });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "require" }); checkElements(element->GetLineNum(), children, {}, { "require" });
if (attributes.find("supported")->second == "disabled") if (attributes.find("supported")->second == "disabled")
{ {
@ -3873,11 +3882,11 @@ void VulkanHppGenerator::readExtensionDisabledRequire(tinyxml2::XMLElement const
{ {
checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); checkAttributes(getAttributes(element), element->GetLineNum(), {}, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "command", "comment", "enum", "type" }); checkElements(element->GetLineNum(), children, { "enum" }, { "command", "comment", "type" });
for (auto child : children) for (auto child : children)
{ {
checkElements(getChildElements(child), {}); checkElements(child->GetLineNum(), getChildElements(child), {});
std::string value = child->Value(); std::string value = child->Value();
if ((value == "command") || (value == "type")) if ((value == "command") || (value == "type"))
@ -3926,7 +3935,7 @@ void VulkanHppGenerator::readExtensionRequire(tinyxml2::XMLElement const* elemen
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "extension",{} },{ "feature",{} } }); checkAttributes(attributes, element->GetLineNum(), {}, { { "extension",{} },{ "feature",{} } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "command", "comment", "enum", "type" }); checkElements(element->GetLineNum(), children, {}, { "command", "comment", "enum", "type" });
for (auto child : children) for (auto child : children)
{ {
@ -3958,7 +3967,7 @@ void VulkanHppGenerator::readExtensionRequireCommand(tinyxml2::XMLElement const*
{ {
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {});
checkElements(getChildElements(element), {}); checkElements(element->GetLineNum(), getChildElements(element), {});
// just add the protect string to the CommandData // just add the protect string to the CommandData
if (!platform.empty()) if (!platform.empty())
@ -3978,7 +3987,7 @@ void VulkanHppGenerator::readExtensionRequireType(tinyxml2::XMLElement const* el
{ {
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {});
checkElements(getChildElements(element), {}); checkElements(element->GetLineNum(), getChildElements(element), {});
// add the protect-string to the appropriate type: enum, flag, handle, scalar, or struct // add the protect-string to the appropriate type: enum, flag, handle, scalar, or struct
std::string name = attributes.find("name")->second; std::string name = attributes.find("name")->second;
@ -3990,10 +3999,8 @@ void VulkanHppGenerator::readExtensionRequireType(tinyxml2::XMLElement const* el
auto objectTypeIt = m_enums.find("VkObjectType"); auto objectTypeIt = m_enums.find("VkObjectType");
assert(objectTypeIt != m_enums.end()); assert(objectTypeIt != m_enums.end());
std::string objectTypeName = "e" + stripPrefix(handleIt->first, "Vk"); std::string objectTypeName = "e" + stripPrefix(handleIt->first, "Vk");
if (std::find_if(objectTypeIt->second.values.begin(), objectTypeIt->second.values.end(), [objectTypeName](EnumValueData const& evd) {return evd.vkValue == objectTypeName; }) == objectTypeIt->second.values.end()) auto valueIt = std::find_if(objectTypeIt->second.values.begin(), objectTypeIt->second.values.end(), [objectTypeName](EnumValueData const& evd) {return evd.vkValue == objectTypeName; });
{ check(valueIt != objectTypeIt->second.values.end(), element->GetLineNum(), "missing entry in VkObjectType enum for handle <" + name + ">.");
throw std::runtime_error("Spec error on line " + std::to_string(element->GetLineNum()) + ": missing entry in VkObjectType enum for handle <" + name + ">.");
}
} }
if (!platform.empty()) if (!platform.empty())
@ -4024,7 +4031,7 @@ void VulkanHppGenerator::readExtensionRequireType(tinyxml2::XMLElement const* el
} }
else else
{ {
assert((m_defines.find(name) != m_defines.end())); assert((m_types.find(name) != m_types.end()));
} }
} }
} }
@ -4036,7 +4043,7 @@ void VulkanHppGenerator::readExtensions(tinyxml2::XMLElement const* element)
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "comment",{} } }, {}); checkAttributes(attributes, element->GetLineNum(), { { "comment",{} } }, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "extension" }); checkElements(element->GetLineNum(), children, { "extension" });
for (auto child : children) for (auto child : children)
{ {
@ -4049,7 +4056,7 @@ void VulkanHppGenerator::readFeature(tinyxml2::XMLElement const* element)
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "api",{ "vulkan" } },{ "comment",{} },{ "name",{} },{ "number",{} } }, {}); checkAttributes(attributes, element->GetLineNum(), { { "api",{ "vulkan" } },{ "comment",{} },{ "name",{} },{ "number",{} } }, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "require" }); checkElements(element->GetLineNum(), children, { "require" });
for (auto child : children) for (auto child : children)
{ {
@ -4062,7 +4069,7 @@ void VulkanHppGenerator::readFeatureRequire(tinyxml2::XMLElement const* element)
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } }); checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "command", "comment", "enum", "type" }); checkElements(element->GetLineNum(), children, {}, { "command", "comment", "enum", "type" });
for (auto child : children) for (auto child : children)
{ {
@ -4078,7 +4085,7 @@ void VulkanHppGenerator::readFuncpointer(tinyxml2::XMLElement const* element, st
{ {
checkAttributes(attributes, element->GetLineNum(), { { "category",{ "funcpointer" } } }, { { "requires",{} } }); checkAttributes(attributes, element->GetLineNum(), { { "category",{ "funcpointer" } } }, { { "requires",{} } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "name", "type" }); checkElements(element->GetLineNum(), children, { "name" }, { "type" });
assert(!children.empty()); assert(!children.empty());
checkEmptyElement(children[0]); checkEmptyElement(children[0]);
@ -4100,8 +4107,8 @@ void VulkanHppGenerator::readHandle(tinyxml2::XMLElement const* element, std::ma
if (aliasIt != attributes.end()) if (aliasIt != attributes.end())
{ {
checkAttributes(attributes, element->GetLineNum(), { { "alias",{} },{ "category",{ "handle" } },{ "name",{} } }, {}); // re-check on alias type! checkAttributes(attributes, element->GetLineNum(), { { "alias",{} },{ "category",{ "handle" } },{ "name",{} } }, {}); // re-check on alias type!
checkElements(children, {}); checkElements(element->GetLineNum(), children, {});
checkAlias(m_handles, aliasIt->second, element->GetLineNum()); check(m_handles.find(aliasIt->second) != m_handles.end(), element->GetLineNum(), "missing alias <" + aliasIt->second + ">.");
auto handlesIt = m_handles.find(aliasIt->second); auto handlesIt = m_handles.find(aliasIt->second);
assert((handlesIt != m_handles.end()) && handlesIt->second.alias.empty()); assert((handlesIt != m_handles.end()) && handlesIt->second.alias.empty());
@ -4127,9 +4134,10 @@ void VulkanHppGenerator::readHandle(tinyxml2::XMLElement const* element, std::ma
void VulkanHppGenerator::readPlatform(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readPlatform(tinyxml2::XMLElement const* element)
{ {
int line = element->GetLineNum();
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "comment",{} },{ "name",{} },{ "protect",{} } }, {}); checkAttributes(attributes, line, { { "comment",{} },{ "name",{} },{ "protect",{} } }, {});
checkElements(getChildElements(element), {}); checkElements(line, getChildElements(element), {});
std::string name, protect; std::string name, protect;
for (auto const& attribute : attributes) for (auto const& attribute : attributes)
@ -4137,18 +4145,17 @@ void VulkanHppGenerator::readPlatform(tinyxml2::XMLElement const* element)
if (attribute.first == "name") if (attribute.first == "name")
{ {
name = attribute.second; name = attribute.second;
check(!name.empty(), line, "attribute <name> is empty");
} }
else if (attribute.first == "protect") else if (attribute.first == "protect")
{ {
protect = attribute.second; protect = attribute.second;
} check(!protect.empty(), line, "attribute <protect> is empty");
else
{
assert(attribute.first == "comment");
} }
} }
assert(!name.empty() && !protect.empty()); assert(!name.empty() && !protect.empty());
assert(m_platforms.find(name) == m_platforms.end()); check(m_platforms.find(name) == m_platforms.end(), line, "platform name <" + name + "> already specified");
check(std::find_if(m_platforms.begin(), m_platforms.end(), [&protect](std::pair<std::string, std::string> const& p) { return p.second == protect; }) == m_platforms.end(), line, "platform protect <" + protect + "> already specified");
m_platforms[name] = protect; m_platforms[name] = protect;
} }
@ -4156,11 +4163,7 @@ void VulkanHppGenerator::readPlatforms(tinyxml2::XMLElement const* element)
{ {
checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "platform" }); checkElements(element->GetLineNum(), children, { "platform" });
// init m_platforms with "" -> ""
assert(m_platforms.empty());
m_platforms[""] = "";
for (auto child : children) for (auto child : children)
{ {
@ -4185,7 +4188,7 @@ void VulkanHppGenerator::readRequireEnum(tinyxml2::XMLElement const* element, st
{ "offset",{} }, { "offset",{} },
{ "value",{} } { "value",{} }
}); });
checkElements(getChildElements(element), {}); checkElements(element->GetLineNum(), getChildElements(element), {});
// TODO process enums which don't extend existing enums // TODO process enums which don't extend existing enums
auto extendsIt = attributes.find("extends"); auto extendsIt = attributes.find("extends");
@ -4231,16 +4234,25 @@ void VulkanHppGenerator::readRequireEnum(tinyxml2::XMLElement const* element, st
} }
} }
#if !defined(NDEBUG)
void VulkanHppGenerator::readRequires(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readRequires(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes)
{ {
checkAttributes(attributes, element->GetLineNum(), { {"name", {}}, { "requires", {}} }, {}); int line = element->GetLineNum();
checkElements(getChildElements(element), {}); checkAttributes(attributes, line, { {"name", {}}, { "requires", {}} }, {});
checkElements(line, getChildElements(element), {});
auto nameIt = attributes.find("name"); for (auto attribute : attributes)
assert(nameIt != attributes.end()); {
if (attribute.first == "name")
{
check(m_types.insert(attribute.second).second, line, "type named <" + attribute.second + "> already specified");
}
else
{
assert(attribute.first == "requires");
check(m_includes.find(attribute.second) != m_includes.end(), line, "type requires unknown include <" + attribute.second + ">");
}
}
} }
#endif
void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map<std::string, std::string> const& attributes)
{ {
@ -4256,7 +4268,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is
{ "structextends",{} } { "structextends",{} }
}); });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "comment", "member" }); checkElements(element->GetLineNum(), children, {}, { "member", "comment" });
std::string name = attributes.find("name")->second; std::string name = attributes.find("name")->second;
@ -4268,7 +4280,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is
else else
{ {
assert(m_structures.find(name) == m_structures.end()); assert(m_structures.find(name) == m_structures.end());
std::map<std::string, StructureData>::iterator it = m_structures.insert(std::make_pair(name, StructureData())).first; std::map<std::string, StructureData>::iterator it = m_structures.insert(std::make_pair(name, StructureData(element->GetLineNum()))).first;
it->second.returnedOnly = (attributes.find("returnedonly") != attributes.end()); it->second.returnedOnly = (attributes.find("returnedonly") != attributes.end());
it->second.isUnion = isUnion; it->second.isUnion = isUnion;
@ -4281,7 +4293,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is
void VulkanHppGenerator::readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map<std::string, std::string> const& attributes)
{ {
checkAttributes(attributes, lineNum, { { "alias",{} },{ "category",{ "struct" } },{ "name",{} } }, {}); // re-check on alias type! checkAttributes(attributes, lineNum, { { "alias",{} },{ "category",{ "struct" } },{ "name",{} } }, {}); // re-check on alias type!
checkAlias(m_structures, alias, lineNum); check(m_structures.find(alias) != m_structures.end(), lineNum, "missing alias <" + alias + ">.");
auto structsIt = m_structures.find(alias); auto structsIt = m_structures.find(alias);
assert((structsIt != m_structures.end()) && (std::find(structsIt->second.aliases.begin(), structsIt->second.aliases.end(), name) == structsIt->second.aliases.end())); assert((structsIt != m_structures.end()) && (std::find(structsIt->second.aliases.begin(), structsIt->second.aliases.end(), name) == structsIt->second.aliases.end()));
@ -4304,7 +4316,7 @@ VulkanHppGenerator::MemberData VulkanHppGenerator::readStructMember(tinyxml2::XM
{ "values",{} } { "values",{} }
}); });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "comment", "enum", "name", "type" }); checkElements(element->GetLineNum(), children, { "name", "type" }, { "comment", "enum" });
MemberData memberData; MemberData memberData;
auto valuesIt = attributes.find("values"); auto valuesIt = attributes.find("values");
@ -4367,7 +4379,7 @@ void VulkanHppGenerator::readTags(tinyxml2::XMLElement const* element)
{ {
checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "tag" }); checkElements(element->GetLineNum(), children, { "tag" });
for (auto child : children) for (auto child : children)
{ {
@ -4377,28 +4389,28 @@ void VulkanHppGenerator::readTags(tinyxml2::XMLElement const* element)
void VulkanHppGenerator::readTag(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readTag(tinyxml2::XMLElement const* element)
{ {
int line = element->GetLineNum();
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "author",{} },{ "contact",{} },{ "name",{} } }, {}); checkAttributes(attributes, line, { { "author",{} },{ "contact",{} },{ "name",{} } }, {});
checkElements(getChildElements(element), {}); checkElements(line, getChildElements(element), {});
for (auto const& attribute : attributes) for (auto const& attribute : attributes)
{ {
std::string name = attribute.first; if (attribute.first == "name")
if (name == "name")
{ {
std::string value = attribute.second; check(m_tags.find(attribute.second) == m_tags.end(), line, "tag named <" + attribute.second + "> has already been specified");
assert(m_tags.find(value) == m_tags.end()); m_tags.insert(attribute.second);
m_tags.insert(value);
} }
else else
{ {
assert((name == "author") || (name == "contact")); check((attribute.first == "author") || (attribute.first == "contact"), line, "unknown attribute <" + attribute.first + ">");
} }
} }
} }
void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element)
{ {
int line = element->GetLineNum();
std::map<std::string, std::string> attributes = getAttributes(element); std::map<std::string, std::string> attributes = getAttributes(element);
auto categoryIt = attributes.find("category"); auto categoryIt = attributes.find("category");
@ -4428,20 +4440,20 @@ void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element)
{ {
readHandle(element, attributes); readHandle(element, attributes);
} }
else if (categoryIt->second == "include")
{
readTypeInclude(element, attributes);
}
else if (categoryIt->second == "struct") else if (categoryIt->second == "struct")
{ {
readStruct(element, false, attributes); readStruct(element, false, attributes);
} }
else if (categoryIt->second == "union") else
{ {
check(categoryIt->second == "union", element->GetLineNum(), "unknown type category <" + categoryIt->second + ">");
readStruct(element, true, attributes); readStruct(element, true, attributes);
} }
else if ( categoryIt->second != "include")
{
throw std::runtime_error("Spec error on line " + std::to_string(element->GetLineNum()) + ": unknown category <" + categoryIt->second + ">");
} }
}
#if !defined(NDEBUG)
else else
{ {
auto requiresIt = attributes.find("requires"); auto requiresIt = attributes.find("requires");
@ -4451,10 +4463,10 @@ void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element)
} }
else else
{ {
assert((attributes.size() == 1) && (attributes.begin()->first == "name") && (attributes.begin()->second == "int")); check((attributes.size() == 1) && (attributes.begin()->first == "name") && (attributes.begin()->second == "int"), line, "unknown type");
check(m_types.insert(attributes.begin()->second).second, line, "type <" + attributes.begin()->second + "> already specified");
} }
} }
#endif
} }
void VulkanHppGenerator::readTypeEnum(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes) void VulkanHppGenerator::readTypeEnum(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes)
@ -4478,11 +4490,20 @@ void VulkanHppGenerator::readTypeEnum(tinyxml2::XMLElement const* element, std::
} }
} }
void VulkanHppGenerator::readTypeInclude(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes)
{
checkAttributes(attributes, element->GetLineNum(), { { "category", { "include" } }, { "name",{} } }, {});
checkElements(element->GetLineNum(), getChildElements(element), {});
std::string name = attributes.find("name")->second;
check(m_includes.insert(name).second, element->GetLineNum(), "include named <" + name + "> already specified");
}
void VulkanHppGenerator::readTypes(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readTypes(tinyxml2::XMLElement const* element)
{ {
checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {});
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element); std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "comment", "type" }); checkElements(element->GetLineNum(), children, { "comment", "type" });
for (auto child : children) for (auto child : children)
{ {
@ -4491,13 +4512,10 @@ void VulkanHppGenerator::readTypes(tinyxml2::XMLElement const* element)
{ {
readType(child); readType(child);
} }
#if !defined(NDEBUG)
else else
{ {
assert(value == "comment"); readComment(child);
checkEmptyElement(child);
} }
#endif
} }
} }
@ -4550,6 +4568,34 @@ std::string VulkanHppGenerator::TypeData::compose() const
return prefix + (prefix.empty() ? "" : " ") + ((type.substr(0, 2) == "Vk") ? "VULKAN_HPP_NAMESPACE::" : "") + stripPrefix(type, "Vk") + postfix; return prefix + (prefix.empty() ? "" : " ") + ((type.substr(0, 2) == "Vk") ? "VULKAN_HPP_NAMESPACE::" : "") + stripPrefix(type, "Vk") + postfix;
} }
std::string to_string(tinyxml2::XMLError error)
{
switch (error)
{
case tinyxml2::XML_SUCCESS : return "XML_SUCCESS";
case tinyxml2::XML_NO_ATTRIBUTE : return "XML_NO_ATTRIBUTE";
case tinyxml2::XML_WRONG_ATTRIBUTE_TYPE : return "XML_WRONG_ATTRIBUTE_TYPE";
case tinyxml2::XML_ERROR_FILE_NOT_FOUND : return "XML_ERROR_FILE_NOT_FOUND";
case tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED : return "XML_ERROR_FILE_COULD_NOT_BE_OPENED";
case tinyxml2::XML_ERROR_FILE_READ_ERROR : return "XML_ERROR_FILE_READ_ERROR";
case tinyxml2::UNUSED_XML_ERROR_ELEMENT_MISMATCH : return "UNUSED_XML_ERROR_ELEMENT_MISMATCH";
case tinyxml2::XML_ERROR_PARSING_ELEMENT : return "XML_ERROR_PARSING_ELEMENT";
case tinyxml2::XML_ERROR_PARSING_ATTRIBUTE : return "XML_ERROR_PARSING_ATTRIBUTE";
case tinyxml2::UNUSED_XML_ERROR_IDENTIFYING_TAG : return "UNUSED_XML_ERROR_IDENTIFYING_TAG";
case tinyxml2::XML_ERROR_PARSING_TEXT : return "XML_ERROR_PARSING_TEXT";
case tinyxml2::XML_ERROR_PARSING_CDATA : return "XML_ERROR_PARSING_CDATA";
case tinyxml2::XML_ERROR_PARSING_COMMENT : return "XML_ERROR_PARSING_COMMENT";
case tinyxml2::XML_ERROR_PARSING_DECLARATION : return "XML_ERROR_PARSING_DECLARATION";
case tinyxml2::XML_ERROR_PARSING_UNKNOWN : return "XML_ERROR_PARSING_UNKNOWN";
case tinyxml2::XML_ERROR_EMPTY_DOCUMENT : return "XML_ERROR_EMPTY_DOCUMENT";
case tinyxml2::XML_ERROR_MISMATCHED_ELEMENT : return "XML_ERROR_MISMATCHED_ELEMENT";
case tinyxml2::XML_ERROR_PARSING : return "XML_ERROR_PARSING";
case tinyxml2::XML_CAN_NOT_CONVERT_TEXT : return "XML_CAN_NOT_CONVERT_TEXT";
case tinyxml2::XML_NO_TEXT_NODE : return "XML_NO_TEXT_NODE";
default : return "unknown error code <" + std::to_string(error) + ">";
}
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
static const std::string classArrayProxy = R"( static const std::string classArrayProxy = R"(
@ -5674,19 +5720,24 @@ namespace std
tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
if (error != tinyxml2::XML_SUCCESS) if (error != tinyxml2::XML_SUCCESS)
{ {
std::cout << "VkGenerate: failed to load file " << filename << " . Error code: " << error << std::endl; std::cout << "VkGenerate: failed to load file " << filename << " with error <" << to_string(error) << ">" << std::endl;
return -1; return -1;
} }
VulkanHppGenerator generator; VulkanHppGenerator generator;
tinyxml2::XMLElement const* registryElement = doc.FirstChildElement(); tinyxml2::XMLElement const* registryElement = nullptr;
for (tinyxml2::XMLElement const* element = doc.FirstChildElement(); element; element = doc.NextSiblingElement())
{
std::string value = element->Value();
check(value == "registry", element->GetLineNum(), "unknown top level element <" + value + ">");
registryElement = element;
}
assert(registryElement);
checkAttributes(getAttributes(registryElement), registryElement->GetLineNum(), {}, {}); checkAttributes(getAttributes(registryElement), registryElement->GetLineNum(), {}, {});
assert(strcmp(registryElement->Value(), "registry") == 0);
assert(!registryElement->NextSiblingElement());
std::vector<tinyxml2::XMLElement const*> children = getChildElements(registryElement); std::vector<tinyxml2::XMLElement const*> children = getChildElements(registryElement);
checkElements(children, { "commands", "comment", "enums", "extensions", "feature", "tags", "types", "vendorids", "platforms" }); checkElements(registryElement->GetLineNum(), children, { "commands", "comment", "enums", "extensions", "feature", "tags", "types", "platforms" });
for (auto child : children) for (auto child : children)
{ {
const std::string value = child->Value(); const std::string value = child->Value();

View File

@ -52,15 +52,28 @@ class VulkanHppGenerator
void readTypes(tinyxml2::XMLElement const* element); void readTypes(tinyxml2::XMLElement const* element);
private: private:
struct BaseTypeData
{
BaseTypeData(std::string const& type_, int line)
: type(type_)
, xmlLine(line)
{}
std::string type;
int xmlLine;
};
struct BitmaskData struct BitmaskData
{ {
BitmaskData(std::string const& r) BitmaskData(std::string const& r, int line)
: requirement(r) : requirement(r)
, xmlLine(line)
{} {}
std::string requirement; // original vulkan name: VK*FlagBits std::string requirement; // original vulkan name: VK*FlagBits
std::string platform; std::string platform;
std::string alias; // original vulkan name std::string alias; // original vulkan name
int xmlLine;
}; };
struct TypeData struct TypeData
@ -92,8 +105,9 @@ class VulkanHppGenerator
struct CommandData struct CommandData
{ {
CommandData() CommandData(int line)
: isAlias(false) : isAlias(false)
, xmlLine(line)
{} {}
std::vector<ParamData> params; std::vector<ParamData> params;
@ -101,6 +115,7 @@ class VulkanHppGenerator
std::string returnType; std::string returnType;
std::vector<std::string> successCodes; std::vector<std::string> successCodes;
bool isAlias; bool isAlias;
int xmlLine;
}; };
struct EnumValueData struct EnumValueData
@ -147,9 +162,10 @@ class VulkanHppGenerator
struct StructureData struct StructureData
{ {
StructureData() StructureData(int line)
: returnedOnly(false) : returnedOnly(false)
, isUnion(false) , isUnion(false)
, xmlLine(line)
{} {}
bool returnedOnly; bool returnedOnly;
@ -159,6 +175,7 @@ class VulkanHppGenerator
std::vector<std::string> structExtends; std::vector<std::string> structExtends;
std::vector<std::string> aliases; std::vector<std::string> aliases;
std::string subStruct; std::string subStruct;
int xmlLine;
}; };
private: private:
@ -237,6 +254,7 @@ class VulkanHppGenerator
void readHandle(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes); void readHandle(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
void readPlatform(tinyxml2::XMLElement const* element); void readPlatform(tinyxml2::XMLElement const* element);
void readRequireEnum(tinyxml2::XMLElement const* element, std::string const& tag); void readRequireEnum(tinyxml2::XMLElement const* element, std::string const& tag);
void readRequires(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
void readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map<std::string, std::string> const& attributes); void readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map<std::string, std::string> const& attributes);
void readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map<std::string, std::string> const& attributes); void readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map<std::string, std::string> const& attributes);
MemberData readStructMember(tinyxml2::XMLElement const* element); MemberData readStructMember(tinyxml2::XMLElement const* element);
@ -244,29 +262,26 @@ class VulkanHppGenerator
void readTag(tinyxml2::XMLElement const* element); void readTag(tinyxml2::XMLElement const* element);
void readType(tinyxml2::XMLElement const* element); void readType(tinyxml2::XMLElement const* element);
void readTypeEnum(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes); void readTypeEnum(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
void readTypeInclude(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
void registerDeleter(std::string const& name, std::pair<std::string, CommandData> const& commandData); void registerDeleter(std::string const& name, std::pair<std::string, CommandData> const& commandData);
void unlinkCommandFromHandle(std::string const& name); void unlinkCommandFromHandle(std::string const& name);
#if !defined(NDEBUG)
void readRequires(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
#endif
private: private:
std::map<std::string, std::string> m_baseTypes; std::map<std::string, BaseTypeData> m_baseTypes;
std::map<std::string, BitmaskData> m_bitmasks; std::map<std::string, BitmaskData> m_bitmasks;
std::map<std::string, std::string> m_commandToHandle; std::map<std::string, std::string> m_commandToHandle;
std::map<std::string, EnumData> m_enums; std::map<std::string, EnumData> m_enums;
std::set<std::string> m_extendedStructs; // structs which are referenced by the structextends tag std::set<std::string> m_extendedStructs; // structs which are referenced by the structextends tag
std::map<std::string, HandleData> m_handles; std::map<std::string, HandleData> m_handles;
std::set<std::string> m_includes;
std::map<std::string, std::string> m_platforms; std::map<std::string, std::string> m_platforms;
std::map<std::string, std::string> m_structureAliases; std::map<std::string, std::string> m_structureAliases;
std::map<std::string, StructureData> m_structures; std::map<std::string, StructureData> m_structures;
std::set<std::string> m_tags; std::set<std::string> m_tags;
std::set<std::string> m_types;
std::string m_typesafeCheck; std::string m_typesafeCheck;
std::string m_version; std::string m_version;
std::string m_vulkanLicenseHeader; std::string m_vulkanLicenseHeader;
#if !defined(NDEBUG)
std::set<std::string> m_defines; // just used for verfication in readExtensionType
#endif
}; };
const size_t INVALID_INDEX = (size_t)~0; const size_t INVALID_INDEX = (size_t)~0;