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