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