diff --git a/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp b/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp index 7e9c71cbecf3c5dd9d8952135b61625a84ce1a27..1955a76581429fd9b327fce7d3f0d23f9f64e8b8 100644 --- a/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp +++ b/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp @@ -21,25 +21,19 @@ * GNU General Public License */ -// STD/STL - - -// Header #include "Reader.h" + #include <sstream> -// Simox #include <SimoxUtility/algorithm/string/string_tools.h> #include <SimoxUtility/algorithm/vector.hpp> -// ArmarX #include <ArmarXCore/core/rapidxml/wrapper/RapidXmlReader.h> #include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> #include <RobotAPI/libraries/aron/codegeneration/typereader/xml/Data.h> #include <RobotAPI/libraries/aron/core/type/variant/Factory.h> - namespace armarx::aron::typereader::xml { namespace fs = std::filesystem; @@ -47,12 +41,14 @@ namespace armarx::aron::typereader::xml namespace { /// Resolve a relative package path - std::optional<fs::path> resolveRelativePackagePath(const fs::path& path, const std::vector<fs::path>& includePaths) + std::optional<fs::path> + resolveRelativePackagePath(const fs::path& filepath, + const std::vector<fs::path>& includePaths) { // new behavior: using provided include paths for (const auto& includePath : includePaths) { - fs::path absPath = includePath / path; + fs::path absPath = includePath / filepath; if (fs::is_regular_file(absPath)) { // path is valid @@ -61,27 +57,89 @@ namespace armarx::aron::typereader::xml } // legacy behavior: using cmake package finder paths - const std::string package = *path.begin(); - armarx::CMakePackageFinder finder(package); - if (finder.packageFound()) + + std::vector<std::string> packageNameCandidates; + { + auto it = filepath.begin(); + + std::string packageName = *it; + packageNameCandidates.push_back(packageName); + + ++it; + if (it != filepath.end()) + { + packageName += "_" + it->string(); + packageNameCandidates.push_back(packageName); + } + } + + std::vector<armarx::CMakePackageFinder> finders; + size_t foundFinderIndex = 0; + size_t numberOfFoundCandidates = 0; + for (const std::string& packageName : packageNameCandidates) + { + armarx::CMakePackageFinder& finder = finders.emplace_back(packageName); + if (finder.packageFound()) + { + // TODO: In theory, we could also consider whether the requested file exists in + // the found package (see check below). + numberOfFoundCandidates++; + foundFinderIndex = finders.size() - 1; + } + } + + if (numberOfFoundCandidates == 0) + { + std::stringstream msg; + msg << "No package matching the ARON include " << filepath + << " found. Tried CMake project names:"; + for (const std::string& packageName : packageNameCandidates) + { + msg << " '" << packageName << "',"; + } + throw error::AronException(__PRETTY_FUNCTION__, msg.str()); + } + if (numberOfFoundCandidates > 1) { - for (const std::string& includePath : finder.getIncludePathList()) + std::stringstream msg; + msg << "The ARON include " << filepath << " is ambiguous: The following " + << numberOfFoundCandidates + << " CMake projects matching the include were found:"; + ARMARX_CHECK_EQUAL(packageNameCandidates.size(), finders.size()); + for (size_t i = 0; i < packageNameCandidates.size(); ++i) { - fs::path absPath = includePath / path; - if (fs::is_regular_file(absPath)) + if (finders[i].packageFound()) { - // path is valid - return absPath; + msg << "\n- project '" << packageNameCandidates[i] << "' at '" + << finders[i].getPackageDir() << "'"; } } - return std::nullopt; + throw error::AronException(__PRETTY_FUNCTION__, msg.str()); + } + + ARMARX_CHECK_EQUAL(numberOfFoundCandidates, 1); + ARMARX_CHECK_LESS(foundFinderIndex, finders.size()); + + CMakePackageFinder& finder = finders.at(foundFinderIndex); + ARMARX_CHECK(finder.packageFound()); + + for (const std::string& includePath : finder.getIncludePathList()) + { + fs::path absPath = includePath / filepath; + if (fs::is_regular_file(absPath)) + { + // path is valid + return absPath; + } } return std::nullopt; } - } + } // namespace - void Reader::parseFile(const std::string& _filename, const std::vector<std::filesystem::path>& includePaths) + void + Reader::parseFile(const std::string& _filename, + const std::vector<std::filesystem::path>& includePaths) { std::string filename = _filename; @@ -93,7 +151,9 @@ namespace armarx::aron::typereader::xml parseFile(std::filesystem::path(filename), includePaths); } - void Reader::parseFile(const std::filesystem::path& _file, const std::vector<std::filesystem::path>& includePaths) + void + Reader::parseFile(const std::filesystem::path& _file, + const std::vector<std::filesystem::path>& includePaths) { fs::path file = _file; if (not std::filesystem::exists(file)) @@ -102,7 +162,10 @@ namespace armarx::aron::typereader::xml auto p = resolveRelativePackagePath(file, includePaths); if (not p) { - throw error::AronException(__PRETTY_FUNCTION__, "Could not find the file " + file.string() + ". Tried include paths: " + simox::alg::to_string(includePaths)); + throw error::AronException( + __PRETTY_FUNCTION__, + "Could not find the file " + file.string() + + ". Tried include paths: " + simox::alg::to_string(includePaths)); } file = *p; } @@ -112,7 +175,10 @@ namespace armarx::aron::typereader::xml } // private method reading nodes - void Reader::parse(const RapidXmlReaderPtr& reader, const std::filesystem::path& filePath, const std::vector<std::filesystem::path>& includePaths) + void + Reader::parse(const RapidXmlReaderPtr& reader, + const std::filesystem::path& filePath, + const std::vector<std::filesystem::path>& includePaths) { RapidXmlReaderNode root = reader->getRoot(); @@ -132,7 +198,10 @@ namespace armarx::aron::typereader::xml for (const auto& include : (*codeincludes).nodes()) { auto i = readCodeInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->systemIncludes.push_back(i); + if (not i.empty()) + { + this->systemIncludes.push_back(i); + } } } @@ -142,7 +211,10 @@ namespace armarx::aron::typereader::xml for (const auto& include : (*aronincludes).nodes()) { auto i = readAronInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->aronIncludes.push_back(i); + if (not i.empty()) + { + this->aronIncludes.push_back(i); + } } } @@ -151,10 +223,14 @@ namespace armarx::aron::typereader::xml { for (const auto& include : (*includes).nodes()) { - if (util::HasTagName(include, constantes::SYSTEM_INCLUDE_TAG)) // if its a system include tag then we know that it must be a code include + if (util::HasTagName( + include, + constantes:: + SYSTEM_INCLUDE_TAG)) // if its a system include tag then we know that it must be a code include { auto i = readCodeInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->systemIncludes.push_back(i); + if (not i.empty()) + this->systemIncludes.push_back(i); } else { @@ -171,17 +247,20 @@ namespace armarx::aron::typereader::xml what = util::GetAttribute(include, constantes::INCLUDE_ATTRIBUTE_NAME); } - if (not what.empty() && std::filesystem::path(what) != filePath) // did we found something? + if (not what.empty() && + std::filesystem::path(what) != filePath) // did we found something? { if (simox::alg::ends_with(what, ARON_FILE_SUFFIX)) { auto i = readAronInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->aronIncludes.push_back(i); + if (not i.empty()) + this->aronIncludes.push_back(i); } else // we believe that this is a code include since it is not an xml file { auto i = readCodeInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->systemIncludes.push_back(i); + if (not i.empty()) + this->systemIncludes.push_back(i); } } } @@ -195,13 +274,18 @@ namespace armarx::aron::typereader::xml { if (util::HasTagName(generateType, constantes::OBJECT_TAG)) { - for (const auto& additionalInclude : getAdditionalIncludesFromReplacements(generateType, filePath.parent_path())) + for (const auto& additionalInclude : getAdditionalIncludesFromReplacements( + generateType, filePath.parent_path())) { - RapidXmlReaderPtr reader = RapidXmlReader::FromXmlString("<PackagePath package=\""+additionalInclude.first+"\" path=\""+additionalInclude.second+"\"/>"); + RapidXmlReaderPtr reader = RapidXmlReader::FromXmlString( + "<PackagePath package=\"" + additionalInclude.first + "\" path=\"" + + additionalInclude.second + "\"/>"); auto node = reader->getRoot(); auto i = this->readAronInclude(node, filePath, includePaths); - if (not i.empty() && std::find(this->aronIncludes.begin(), this->aronIncludes.end(), i) == this->aronIncludes.end()) + if (not i.empty() && + std::find(this->aronIncludes.begin(), this->aronIncludes.end(), i) == + this->aronIncludes.end()) { this->aronIncludes.push_back(i); } @@ -218,26 +302,37 @@ namespace armarx::aron::typereader::xml if (util::HasTagName(generateType, constantes::OBJECT_TAG)) { const auto nav = readGenerateObject(generateType); - generateObjects.push_back(factory.allGeneratedPublicObjects.at(nav->getObjectName())); + generateObjects.push_back( + factory.allGeneratedPublicObjects.at(nav->getObjectName())); continue; } if (util::HasTagName(generateType, constantes::INT_ENUM_TAG)) { const auto nav = readGenerateIntEnum(generateType); - generateIntEnums.push_back(factory.allGeneratedPublicIntEnums.at(nav->getEnumName())); + generateIntEnums.push_back( + factory.allGeneratedPublicIntEnums.at(nav->getEnumName())); continue; } - throw error::ValueNotValidException("XMLReader", "parse", "Could not find a valid tag inside generatetypes", generateType.name()); + throw error::ValueNotValidException( + "XMLReader", + "parse", + "Could not find a valid tag inside generatetypes", + generateType.name()); } } else { - throw error::AronException(__PRETTY_FUNCTION__, "No generate types found in aron xml '" + filePath.string() + "'."); + throw error::AronException(__PRETTY_FUNCTION__, + "No generate types found in aron xml '" + filePath.string() + + "'."); } } - std::pair<std::string, std::string> Reader::readPackagePathInclude(const RapidXmlReaderNode& node, const std::filesystem::path&, const std::vector<std::filesystem::path>& includePaths) + std::pair<std::string, std::string> + Reader::readPackagePathInclude(const RapidXmlReaderNode& node, + const std::filesystem::path&, + const std::vector<std::filesystem::path>& includePaths) { util::EnforceTagName(node, constantes::PACKAGE_PATH_TAG); std::string package = util::GetAttribute(node, constantes::PACKAGE_ATTRIBUTE_NAME); @@ -250,15 +345,23 @@ namespace armarx::aron::typereader::xml path = simox::alg::replace_all(path, ">", ""); const std::filesystem::path includepath(package + "/" + path); - if (std::optional<fs::path> resolvedPackagePath = resolveRelativePackagePath(includepath, includePaths); resolvedPackagePath.has_value()) + if (std::optional<fs::path> resolvedPackagePath = + resolveRelativePackagePath(includepath, includePaths); + resolvedPackagePath.has_value()) { return {package + "/" + path, "<" + package + "/" + path + ">"}; } - throw error::AronException(__PRETTY_FUNCTION__, "Could not find an file `" + includepath.string() + "`. Search paths are: " + simox::alg::to_string(includePaths)); + throw error::AronException( + __PRETTY_FUNCTION__, + "Could not find an file `" + includepath.string() + + "`. Search paths are: " + simox::alg::to_string(includePaths)); } - std::pair<std::string, std::string> Reader::readInclude(const RapidXmlReaderNode& node, const std::filesystem::path& filepath, const std::vector<std::filesystem::path>& includePaths) + std::pair<std::string, std::string> + Reader::readInclude(const RapidXmlReaderNode& node, + const std::filesystem::path& filepath, + const std::vector<std::filesystem::path>& includePaths) { util::EnforceTagName(node, constantes::INCLUDE_TAG); std::string value = util::GetAttribute(node, constantes::INCLUDE_ATTRIBUTE_NAME); @@ -270,7 +373,10 @@ namespace armarx::aron::typereader::xml return {value, "<" + value + ">"}; } - std::pair<std::string, std::string> Reader::readSystemInclude(const RapidXmlReaderNode& node, const std::filesystem::path&, const std::vector<std::filesystem::path>& includePaths) + std::pair<std::string, std::string> + Reader::readSystemInclude(const RapidXmlReaderNode& node, + const std::filesystem::path&, + const std::vector<std::filesystem::path>& includePaths) { util::EnforceTagName(node, constantes::SYSTEM_INCLUDE_TAG); std::string value = util::GetAttribute(node, constantes::INCLUDE_ATTRIBUTE_NAME); @@ -282,7 +388,10 @@ namespace armarx::aron::typereader::xml return {"", "<" + value + ">"}; } - std::string Reader::readCodeInclude(const RapidXmlReaderNode& node, const std::filesystem::path& filepath, const std::vector<std::filesystem::path>& includePaths) + std::string + Reader::readCodeInclude(const RapidXmlReaderNode& node, + const std::filesystem::path& filepath, + const std::vector<std::filesystem::path>& includePaths) { if (util::HasTagName(node, constantes::PACKAGE_PATH_TAG)) { @@ -297,10 +406,14 @@ namespace armarx::aron::typereader::xml return readSystemInclude(node, filepath, includePaths).second; } - throw error::ValueNotValidException(__PRETTY_FUNCTION__, "Could not parse a code include. The tag is wrong.", node.name()); + throw error::ValueNotValidException( + __PRETTY_FUNCTION__, "Could not parse a code include. The tag is wrong.", node.name()); } - std::string Reader::readAronInclude(const RapidXmlReaderNode& node, const std::filesystem::path& filepath, const std::vector<std::filesystem::path>& includePaths) + std::string + Reader::readAronInclude(const RapidXmlReaderNode& node, + const std::filesystem::path& filepath, + const std::vector<std::filesystem::path>& includePaths) { std::string include; std::string include_unescaped; @@ -320,7 +433,8 @@ namespace armarx::aron::typereader::xml if (not include.empty()) // we found something { // autoinclude the code file (suffix will be replaced by correct language) - std::string codeinclude = simox::alg::replace_last(include, ARON_FILE_SUFFIX, CODE_FILE_SUFFIX); + std::string codeinclude = + simox::alg::replace_last(include, ARON_FILE_SUFFIX, CODE_FILE_SUFFIX); this->systemIncludes.push_back(codeinclude); // parse parent xml file and add objects to alreday known @@ -342,17 +456,22 @@ namespace armarx::aron::typereader::xml return include; } - throw error::ValueNotValidException(__PRETTY_FUNCTION__, "Could not parse an aron include. The tag is wrong.", node.name()); + throw error::ValueNotValidException( + __PRETTY_FUNCTION__, "Could not parse an aron include. The tag is wrong.", node.name()); } - std::vector<std::pair<std::string, std::string>> Reader::getAdditionalIncludesFromReplacements(const RapidXmlReaderNode& node, const std::filesystem::path& filePath) + std::vector<std::pair<std::string, std::string>> + Reader::getAdditionalIncludesFromReplacements(const RapidXmlReaderNode& node, + const std::filesystem::path& filePath) { std::vector<std::pair<std::string, std::string>> ret; for (const auto& repl : constantes::REPLACEMENTS) { auto replacement = repl.second; - if (not replacement.additionalAronDTOXMLIncludePackagePath.first.empty() && not replacement.additionalAronDTOXMLIncludePackagePath.second.empty() && util::HasTagName(node, repl.first)) + if (not replacement.additionalAronDTOXMLIncludePackagePath.first.empty() && + not replacement.additionalAronDTOXMLIncludePackagePath.second.empty() && + util::HasTagName(node, repl.first)) { // we found a string that will be replaced so we might need to add a default include ret.push_back(replacement.additionalAronDTOXMLIncludePackagePath); @@ -374,15 +493,17 @@ namespace armarx::aron::typereader::xml return ret; } - type::ObjectPtr Reader::readGenerateObject(const RapidXmlReaderNode& node) + type::ObjectPtr + Reader::readGenerateObject(const RapidXmlReaderNode& node) { util::EnforceTagName(node, constantes::OBJECT_TAG); return type::Object::DynamicCastAndCheck(factory.create(node, Path())); } - type::IntEnumPtr Reader::readGenerateIntEnum(const RapidXmlReaderNode& node) + type::IntEnumPtr + Reader::readGenerateIntEnum(const RapidXmlReaderNode& node) { util::EnforceTagName(node, constantes::INT_ENUM_TAG); return type::IntEnum::DynamicCastAndCheck(factory.create(node, Path())); } -} +} // namespace armarx::aron::typereader::xml