LCOV - code coverage report
Current view: top level - src/esys/repo/grepo - manifestimpl_grepo.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 220 243 90.5 %
Date: 2026-08-12 14:26:50 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/grepo/manifestimpl_grepo.cpp
       3             :  * \brief
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2020-2021 Michel Gillet
       9             :  * Distributed under the MIT License.
      10             :  * (See accompanying file LICENSE.txt or
      11             :  * copy at https://opensource.org/licenses/MIT)
      12             :  *
      13             :  * __legal_e__
      14             :  * \endcond
      15             :  *
      16             :  */
      17             : 
      18             : #include "esys/repo/esysrepo_prec.h"
      19             : #include "esys/repo/grepo/manifestimpl.h"
      20             : #include "esys/repo/grepo/manifest.h"
      21             : #include "esys/repo/manifest/repository.h"
      22             : #include "esys/repo/manifest/syncs.h"
      23             : 
      24             : #include <esysfile/xml/writer.h>
      25             : #include <esysfile/xml/attr.h>
      26             : 
      27             : #include <boost/filesystem.hpp>
      28             : #include <boost/tokenizer.hpp>
      29             : 
      30             : namespace esys::repo::grepo
      31             : {
      32             : 
      33          29 : ManifestImpl::ManifestImpl(Manifest *self)
      34          29 :     : m_self(self)
      35             : {
      36          29 : }
      37             : 
      38          85 : ManifestImpl::~ManifestImpl() = default;
      39             : 
      40          27 : Result ManifestImpl::read(const std::string &filename)
      41             : {
      42          27 :     m_repo_no_locations.clear();
      43             : 
      44          27 :     m_file = std::make_shared<esysfile::xml::File>();
      45          27 :     m_file->set_root_node_name("manifest");
      46             : 
      47          54 :     if (self()->get_data() == nullptr) self()->set_data(std::make_shared<repo::Manifest>());
      48             : 
      49          27 :     self()->get_data()->clear();
      50             : 
      51          27 :     int result = m_file->read(filename);
      52          27 :     if (result < 0) return ESYSREPO_RESULT(ResultCode::MANIFEST_ERROR_PARSING_FILE, filename);
      53             : 
      54          27 :     self()->get_data()->set_type(manifest::Type::GOOGLE_MANIFEST);
      55          27 :     self()->get_data()->set_kind(manifest::Kind::ISOLATED);
      56             : 
      57          54 :     return read(m_file->get_data());
      58             : }
      59             : 
      60           4 : Result ManifestImpl::write_xml()
      61             : {
      62           8 :     if (self()->get_data() == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_XML_DATA_NULLPTR);
      63             : 
      64           4 :     m_xml_data = std::make_shared<esysfile::xml::Data>();
      65             : 
      66           4 :     m_xml_data->set_root_node_name("manifest");
      67             : 
      68           4 :     write_remotes();
      69          12 :     write_default(m_xml_data, self()->get_data());
      70           4 :     Result result = write_projects();
      71           8 :     return ESYSREPO_RESULT(result);
      72           4 : }
      73             : 
      74           2 : Result ManifestImpl::write(const std::string &filename)
      75             : {
      76           2 :     Result result = write_xml();
      77           2 :     if (result.error()) return ESYSREPO_RESULT(result);
      78             : 
      79           2 :     esysfile::xml::Writer writer;
      80             : 
      81           4 :     writer.set_data(m_xml_data);
      82             : 
      83           2 :     int result_int = writer.write(filename);
      84           2 :     if (result_int < 0) return ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result_int);
      85           2 :     return ESYSREPO_RESULT(ResultCode::OK);
      86           2 : }
      87             : 
      88           1 : Result ManifestImpl::write(std::ostream &os)
      89             : {
      90           1 :     Result result = write_xml();
      91           1 :     if (write_xml().error()) return ESYSREPO_RESULT(result);
      92             : 
      93           1 :     esysfile::xml::Writer writer;
      94             : 
      95           1 :     writer.set_indent(4);
      96           2 :     writer.set_data(m_xml_data);
      97             : 
      98           1 :     int result_int = writer.write(os);
      99           1 :     if (result_int < 0) return ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result_int);
     100           1 :     return ESYSREPO_RESULT(ResultCode::OK);
     101           1 : }
     102             : 
     103           4 : void ManifestImpl::write_remotes()
     104             : {
     105          12 :     for (auto &remote : self()->get_data()->get_locations())
     106             :     {
     107          20 :         write_remote(m_xml_data, remote);
     108             :     }
     109           4 : }
     110             : 
     111           4 : void ManifestImpl::write_remote(std::shared_ptr<esysfile::xml::Element> parent,
     112             :                                 std::shared_ptr<manifest::Location> location)
     113             : {
     114           4 :     std::shared_ptr<esysfile::xml::Element> remote_el;
     115             : 
     116           8 :     remote_el = std::make_shared<esysfile::xml::Element>();
     117             : 
     118           4 :     remote_el->set_name("remote");
     119           4 :     remote_el->add_attr("name", location->get_name());
     120           4 :     remote_el->add_attr("fetch", location->get_address());
     121             : 
     122           8 :     parent->add_element(remote_el);
     123           4 : }
     124             : 
     125           4 : void ManifestImpl::write_default(std::shared_ptr<esysfile::xml::Element> parent,
     126             :                                  std::shared_ptr<repo::Manifest> manifest)
     127             : {
     128           4 :     std::shared_ptr<esysfile::xml::Element> default_el;
     129             : 
     130           8 :     default_el = std::make_shared<esysfile::xml::Element>();
     131             : 
     132           4 :     default_el->set_name("default");
     133           8 :     if (!manifest->get_default_revision().empty()) default_el->add_attr("revision", manifest->get_default_revision());
     134          16 :     if (manifest->get_default_location()) default_el->add_attr("remote", manifest->get_default_location()->get_name());
     135           7 :     if (manifest->get_default_job_count() != 1) default_el->add_attr("sync-j", manifest->get_default_job_count());
     136           4 :     if (manifest->get_default_sync_s().has_value())
     137           0 :         default_el->add_attr("sync-s", *manifest->get_default_sync_s() ? "true" : "false");
     138             : 
     139          12 :     parent->add_element(default_el);
     140           4 : }
     141             : 
     142           4 : Result ManifestImpl::write_projects()
     143             : {
     144          12 :     for (auto &remote : self()->get_data()->get_locations())
     145             :     {
     146          29 :         for (auto &project : remote->get_repos())
     147             :         {
     148         100 :             Result result = write_project(m_xml_data, project);
     149          25 :             if (result.error()) return ESYSREPO_RESULT(result);
     150          25 :         }
     151             :     }
     152           4 :     return ESYSREPO_RESULT(ResultCode::OK);
     153             : }
     154             : 
     155          25 : Result ManifestImpl::write_project(std::shared_ptr<esysfile::xml::Element> parent,
     156             :                                    std::shared_ptr<manifest::Repository> repository)
     157             : {
     158          25 :     std::shared_ptr<esysfile::xml::Element> project_el;
     159             : 
     160          50 :     project_el = std::make_shared<esysfile::xml::Element>();
     161             : 
     162          25 :     project_el->set_name("project");
     163             : 
     164          25 :     if (repository->get_path().empty()) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_PATH);
     165          25 :     project_el->add_attr("path", repository->get_path());
     166             : 
     167          25 :     if (repository->get_location() == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_LOCATION);
     168             : 
     169          25 :     bool add_location = true;
     170             : 
     171          75 :     if (self()->get_data()->get_default_location() != nullptr)
     172             :     {
     173          75 :         if (repository->get_location()->get_name() == self()->get_data()->get_default_location()->get_name())
     174             :             add_location = false;
     175             :     }
     176           0 :     if (add_location) project_el->add_attr("remote", repository->get_location()->get_name());
     177             : 
     178          25 :     if (repository->get_name().empty()) return ESYSREPO_RESULT(ResultCode::MANIFEST_REPOSITORY_WITHOUT_NAME);
     179          25 :     project_el->add_attr("name", repository->get_name());
     180             : 
     181          37 :     if (!repository->get_revision().empty()) project_el->add_attr("revision", repository->get_revision());
     182             : 
     183          25 :     if (repository->get_sync_s().has_value())
     184           0 :         project_el->add_attr("sync-s", *repository->get_sync_s() ? "true" : "false");
     185             : 
     186          25 :     auto &groups = repository->get_groups();
     187          25 :     if (groups.size() != 0)
     188             :     {
     189          15 :         std::string groups_str;
     190             : 
     191          33 :         for (auto group : groups)
     192             :         {
     193          18 :             if (groups_str.size() != 0) groups_str += ",";
     194             : 
     195          36 :             groups_str += group->get_name();
     196             :         }
     197             : 
     198          30 :         project_el->add_attr("groups", groups_str);
     199          15 :     }
     200             : 
     201          50 :     parent->add_element(project_el);
     202          25 :     return ESYSREPO_RESULT(ResultCode::OK);
     203          25 : }
     204             : 
     205          27 : Result ManifestImpl::read(std::shared_ptr<esysfile::xml::Data> data)
     206             : {
     207          27 :     std::shared_ptr<manifest::Location> location;
     208             : 
     209         288 :     for (auto element : data->get_elements())
     210             :     {
     211         524 :         Result result = read(element);
     212         263 :         if (result.error()) return ESYSREPO_RESULT(result);
     213         524 :     }
     214             : 
     215          26 :     for (auto it = m_repo_no_locations.begin(); it != m_repo_no_locations.end();)
     216             :     {
     217           0 :         location = self()->get_data()->find_location((*it)->get_name());
     218           0 :         if (location != nullptr)
     219             :         {
     220           0 :             location->add_repo(*it);
     221           0 :             m_repo_no_locations.erase(it);
     222             :         }
     223             :         else
     224           0 :             ++it;
     225             :     }
     226             : 
     227          26 :     if (m_repo_no_locations.size() != 0) return ESYSREPO_RESULT(ResultCode::MANIFEST_NO_LOCATIONS_DEFINED);
     228          26 :     return ESYSREPO_RESULT(ResultCode::OK);
     229          27 : }
     230             : 
     231         262 : Result ManifestImpl::read(std::shared_ptr<esysfile::xml::Element> el)
     232             : {
     233         262 :     Result result;
     234             : 
     235         262 :     if (el->get_name() == "remote")
     236          84 :         result = read_remote(el);
     237         234 :     else if (el->get_name() == "default")
     238          72 :         result = read_default(el);
     239         210 :     else if (el->get_name() == "project")
     240         603 :         result = read_project(el);
     241           9 :     else if (el->get_name() == "include")
     242           6 :         result = read_include(el);
     243           7 :     else if (el->get_name() != "#comment")
     244           1 :         result = unknown_element(el->get_name());
     245         786 :     return ESYSREPO_RESULT(result);
     246         262 : }
     247             : 
     248           1 : Result ManifestImpl::unknown_element(const std::string &name)
     249             : {
     250           1 :     if (self()->get_parse_mode() == manifest::ParseMode::Tolerant) return ESYSREPO_RESULT(ResultCode::OK);
     251           0 :     return ESYSREPO_RESULT(ResultCode::MANIFEST_ELEMENT_UNKNOWN, name);
     252             : }
     253             : 
     254           8 : Result ManifestImpl::unknown_attribute(const std::string &element_name, const std::string &attr_name)
     255             : {
     256           8 :     if (self()->get_parse_mode() == manifest::ParseMode::Tolerant) return ESYSREPO_RESULT(ResultCode::OK);
     257           1 :     return ESYSREPO_RESULT(ResultCode::MANIFEST_ATTRIBUTE_UNKNOWN, element_name + ": " + attr_name);
     258             : }
     259             : 
     260          28 : Result ManifestImpl::read_remote(std::shared_ptr<esysfile::xml::Element> el)
     261             : {
     262          28 :     auto location = std::make_shared<manifest::Location>();
     263             : 
     264          86 :     for (auto attr : el->get_attrs())
     265             :     {
     266          59 :         if (attr->get_name() == "name")
     267          28 :             location->set_name(attr->get_value());
     268          31 :         else if (attr->get_name() == "fetch")
     269          28 :             location->set_address(attr->get_value());
     270             :         else
     271             :         {
     272           3 :             Result result = unknown_attribute(el->get_name(), attr->get_name());
     273           4 :             if (result.error()) return ESYSREPO_RESULT(result);
     274           3 :         }
     275          59 :     }
     276             : 
     277          81 :     self()->get_data()->add_location(location);
     278             : 
     279          27 :     return ESYSREPO_RESULT(ResultCode::OK);
     280          28 : }
     281             : 
     282          24 : Result ManifestImpl::read_default(std::shared_ptr<esysfile::xml::Element> el)
     283             : {
     284          98 :     for (auto attr : el->get_attrs())
     285             :     {
     286          74 :         if (attr->get_name() == "revision")
     287          48 :             self()->get_data()->set_default_revision(attr->get_value());
     288          50 :         else if (attr->get_name() == "remote")
     289          48 :             self()->get_data()->set_default_location(attr->get_value());
     290          26 :         else if (attr->get_name() == "sync-j")
     291             :         {
     292          24 :             int value = 0;
     293          24 :             int result = attr->get_value(value);
     294          24 :             if (result < 0)
     295           0 :                 return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE,
     296             :                                        el->get_name() + "." + attr->get_name());
     297          48 :             self()->get_data()->set_default_job_count(value);
     298             :         }
     299           2 :         else if (attr->get_name() == "sync-s")
     300             :         {
     301           0 :             bool sync_s = false;
     302           0 :             if (!manifest::parse_sync_s_value(attr->get_value(), sync_s))
     303           0 :                 return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE,
     304             :                                        el->get_name() + "." + attr->get_name());
     305           0 :             self()->get_data()->set_default_sync_s(sync_s);
     306             :         }
     307             :         else
     308             :         {
     309           2 :             Result result = unknown_attribute(el->get_name(), attr->get_name());
     310           2 :             if (result.error()) return ESYSREPO_RESULT(result);
     311           2 :         }
     312          74 :     }
     313          24 :     return ESYSREPO_RESULT(ResultCode::OK);
     314             : }
     315             : 
     316          75 : Result ManifestImpl::read_groups(std::shared_ptr<manifest::Repository> project, const std::string &groups_str)
     317             : {
     318          75 :     typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
     319          75 :     boost::char_separator<char> sep(", ");
     320          75 :     tokenizer tokens(groups_str, sep);
     321             : 
     322         165 :     for (tokenizer::iterator it = tokens.begin(); it != tokens.end(); ++it)
     323             :     {
     324          90 :         auto group = self()->get_data()->get_groups().find_or_new_group_by_name(*it);
     325          90 :         project->get_groups().push_back(group.get());
     326         270 :         group->add_repo(project);
     327         165 :     }
     328          75 :     return ESYSREPO_RESULT(ResultCode::OK);
     329         150 : }
     330             : 
     331         201 : Result ManifestImpl::read_project(std::shared_ptr<esysfile::xml::Element> el)
     332             : {
     333         201 :     auto project = std::make_shared<manifest::Repository>();
     334         201 :     std::shared_ptr<manifest::Location> location;
     335             : 
     336         822 :     for (auto attr : el->get_attrs())
     337             :     {
     338         621 :         if (attr->get_name() == "name")
     339         201 :             project->set_name(attr->get_value());
     340         420 :         else if (attr->get_name() == "path")
     341         201 :             project->set_path(attr->get_value());
     342         219 :         else if (attr->get_name() == "revision")
     343          88 :             project->set_revision(attr->get_value());
     344         131 :         else if (attr->get_name() == "groups")
     345             :         {
     346         150 :             Result result = read_groups(project, attr->get_value());
     347             :             //! \TODO handle errors
     348          75 :         }
     349          56 :         else if (attr->get_name() == "remote")
     350             :         {
     351         106 :             location = self()->get_data()->find_location(attr->get_value());
     352          53 :             project->set_location(attr->get_value());
     353             : 
     354          53 :             if (location == nullptr) m_repo_no_locations.push_back(project);
     355             :         }
     356           3 :         else if (attr->get_name() == "sync-s")
     357             :         {
     358           0 :             bool sync_s = false;
     359           0 :             if (!manifest::parse_sync_s_value(attr->get_value(), sync_s))
     360           0 :                 return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE,
     361             :                                        el->get_name() + "." + attr->get_name());
     362           0 :             project->set_sync_s(sync_s);
     363             :         }
     364             :         else
     365             :         {
     366           3 :             Result result = unknown_attribute(el->get_name(), attr->get_name());
     367           3 :             if (result.error()) return ESYSREPO_RESULT(result);
     368           3 :         }
     369         621 :     }
     370             : 
     371         201 :     if (location == nullptr)
     372             :     {
     373             :         // It means that the default remote is to be used.
     374         444 :         if (self()->get_data()->get_default_location() != nullptr)
     375         740 :             self()->get_data()->get_default_location()->add_repo(project);
     376             :         else
     377           0 :             return ESYSREPO_RESULT(ResultCode::MANIFEST_NO_DEFAULT_LOCATION);
     378             :     }
     379             :     else
     380         159 :         location->add_repo(project);
     381             : 
     382         201 :     return ESYSREPO_RESULT(ResultCode::OK);
     383         402 : }
     384             : 
     385           2 : Result ManifestImpl::read_include(std::shared_ptr<esysfile::xml::Element> el)
     386             : {
     387           2 :     if (el->get_name() != "include") return ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR);
     388             : 
     389           2 :     auto include = std::make_shared<manifest::Include>();
     390           2 :     std::string name;
     391           2 :     std::string value;
     392             : 
     393           4 :     for (auto attr : el->get_attrs())
     394             :     {
     395           2 :         name = attr->get_name();
     396           2 :         value = attr->get_value();
     397           2 :         if (name == "name")
     398             :         {
     399           2 :             if (boost::filesystem::exists(value))
     400             :             {
     401           0 :                 include->set_name(value);
     402             :             }
     403             :             else
     404             :             {
     405             :                 // Here we assume that the include is relative to the folder "manifests"
     406           2 :                 boost::filesystem::path full_path = self()->get_filename();
     407           2 :                 boost::filesystem::path rel_path = "manifests";
     408           4 :                 full_path = full_path.parent_path();
     409           2 :                 rel_path /= value;
     410           2 :                 full_path /= rel_path;
     411             : 
     412           2 :                 if (boost::filesystem::exists(full_path))
     413           2 :                     include->set_name(full_path.string());
     414             :                 else
     415             :                 {
     416             :                     // TBD: ERROR
     417             :                 }
     418           2 :             }
     419             :         }
     420             :         else
     421             :         {
     422           0 :             Result result = unknown_attribute(el->get_name(), name);
     423           0 :             if (result.error()) return ESYSREPO_RESULT(result);
     424           0 :         }
     425           2 :     }
     426           6 :     self()->get_data()->add_include(include);
     427           2 :     return ESYSREPO_RESULT(ResultCode::OK);
     428           4 : }
     429             : 
     430         733 : Manifest *ManifestImpl::self() const
     431             : {
     432         733 :     return m_self;
     433             : }
     434             : 
     435             : } // namespace esys::repo::grepo

Generated by: LCOV version 1.14