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 42 : ManifestImpl::ManifestImpl(Manifest *self) 34 42 : : m_self(self) 35 : { 36 42 : } 37 : 38 124 : ManifestImpl::~ManifestImpl() = default; 39 : 40 40 : Result ManifestImpl::read(const std::string &filename) 41 : { 42 40 : m_repo_no_locations.clear(); 43 : 44 40 : m_file = std::make_shared<esysfile::xml::File>(); 45 40 : m_file->set_root_node_name("manifest"); 46 : 47 80 : if (self()->get_data() == nullptr) self()->set_data(std::make_shared<repo::Manifest>()); 48 : 49 40 : self()->get_data()->clear(); 50 : 51 40 : int result = m_file->read(filename); 52 40 : if (result < 0) return ESYSREPO_RESULT(ResultCode::MANIFEST_ERROR_PARSING_FILE, filename); 53 : 54 40 : self()->get_data()->set_type(manifest::Type::GOOGLE_MANIFEST); 55 40 : self()->get_data()->set_kind(manifest::Kind::ISOLATED); 56 : 57 80 : 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 40 : Result ManifestImpl::read(std::shared_ptr<esysfile::xml::Data> data) 206 : { 207 40 : std::shared_ptr<manifest::Location> location; 208 : 209 406 : for (auto element : data->get_elements()) 210 : { 211 734 : Result result = read(element); 212 368 : if (result.error()) return ESYSREPO_RESULT(result); 213 734 : } 214 : 215 39 : 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 39 : if (m_repo_no_locations.size() != 0) return ESYSREPO_RESULT(ResultCode::MANIFEST_NO_LOCATIONS_DEFINED); 228 39 : return ESYSREPO_RESULT(ResultCode::OK); 229 40 : } 230 : 231 367 : Result ManifestImpl::read(std::shared_ptr<esysfile::xml::Element> el) 232 : { 233 367 : Result result; 234 : 235 367 : if (el->get_name() == "remote") 236 123 : result = read_remote(el); 237 326 : else if (el->get_name() == "default") 238 111 : result = read_default(el); 239 289 : else if (el->get_name() == "project") 240 840 : 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 1101 : return ESYSREPO_RESULT(result); 246 367 : } 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 41 : Result ManifestImpl::read_remote(std::shared_ptr<esysfile::xml::Element> el) 261 : { 262 41 : auto location = std::make_shared<manifest::Location>(); 263 : 264 125 : for (auto attr : el->get_attrs()) 265 : { 266 85 : if (attr->get_name() == "name") 267 41 : location->set_name(attr->get_value()); 268 44 : else if (attr->get_name() == "fetch") 269 41 : 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 85 : } 276 : 277 120 : self()->get_data()->add_location(location); 278 : 279 40 : return ESYSREPO_RESULT(ResultCode::OK); 280 41 : } 281 : 282 37 : Result ManifestImpl::read_default(std::shared_ptr<esysfile::xml::Element> el) 283 : { 284 150 : for (auto attr : el->get_attrs()) 285 : { 286 113 : if (attr->get_name() == "revision") 287 74 : self()->get_data()->set_default_revision(attr->get_value()); 288 76 : else if (attr->get_name() == "remote") 289 74 : self()->get_data()->set_default_location(attr->get_value()); 290 39 : else if (attr->get_name() == "sync-j") 291 : { 292 37 : int value = 0; 293 37 : int result = attr->get_value(value); 294 37 : if (result < 0) 295 0 : return ESYSREPO_RESULT(ResultCode::MANIFEST_INCORRECT_ATTRIBUTE_VALUE, 296 : el->get_name() + "." + attr->get_name()); 297 74 : 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 113 : } 313 37 : return ESYSREPO_RESULT(ResultCode::OK); 314 : } 315 : 316 140 : Result ManifestImpl::read_groups(std::shared_ptr<manifest::Repository> project, const std::string &groups_str) 317 : { 318 140 : typedef boost::tokenizer<boost::char_separator<char>> tokenizer; 319 140 : boost::char_separator<char> sep(", "); 320 140 : tokenizer tokens(groups_str, sep); 321 : 322 308 : for (tokenizer::iterator it = tokens.begin(); it != tokens.end(); ++it) 323 : { 324 168 : auto group = self()->get_data()->get_groups().find_or_new_group_by_name(*it); 325 168 : project->get_groups().push_back(group.get()); 326 504 : group->add_repo(project); 327 308 : } 328 140 : return ESYSREPO_RESULT(ResultCode::OK); 329 280 : } 330 : 331 280 : Result ManifestImpl::read_project(std::shared_ptr<esysfile::xml::Element> el) 332 : { 333 280 : auto project = std::make_shared<manifest::Repository>(); 334 280 : std::shared_ptr<manifest::Location> location; 335 : 336 1189 : for (auto attr : el->get_attrs()) 337 : { 338 909 : if (attr->get_name() == "name") 339 280 : project->set_name(attr->get_value()); 340 629 : else if (attr->get_name() == "path") 341 280 : project->set_path(attr->get_value()); 342 349 : else if (attr->get_name() == "revision") 343 153 : project->set_revision(attr->get_value()); 344 196 : else if (attr->get_name() == "groups") 345 : { 346 280 : Result result = read_groups(project, attr->get_value()); 347 : //! \TODO handle errors 348 140 : } 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 909 : } 370 : 371 280 : if (location == nullptr) 372 : { 373 : // It means that the default remote is to be used. 374 681 : if (self()->get_data()->get_default_location() != nullptr) 375 1135 : 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 280 : return ESYSREPO_RESULT(ResultCode::OK); 383 560 : } 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 1073 : Manifest *ManifestImpl::self() const 431 : { 432 1073 : return m_self; 433 : } 434 : 435 : } // namespace esys::repo::grepo