Line data Source code
1 : /*!
2 : * \file esys/repo/exe/cmdinit.cpp
3 : * \brief
4 : *
5 : * \cond
6 : * __legal_b__
7 : *
8 : * Copyright (c) 2020-2023 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/exe/cmdinit.h"
20 : #include "esys/repo/filesystem.h"
21 : #include "esys/repo/git/url.h"
22 : #include "esys/repo/manifest/base.h"
23 : #include "esys/repo/manifest/file.h"
24 : #include "esys/repo/manifest/xmlfile.h"
25 : #include "esys/repo/grepo/manifest.h"
26 : #include "esys/repo/manifest/directorymngr.h"
27 :
28 : #include <esys/trace/call.h>
29 : #include <esys/trace/macros.h>
30 :
31 : #include <boost/filesystem.hpp>
32 : #include <boost/system/error_code.hpp>
33 :
34 : #include <cassert>
35 : #include <iostream>
36 : #include <sstream>
37 :
38 : namespace esys::repo::exe
39 : {
40 :
41 50 : CmdInit::CmdInit()
42 100 : : Cmd("Init")
43 : {
44 50 : }
45 :
46 51 : CmdInit::~CmdInit() = default;
47 :
48 52 : void CmdInit::set_url(const std::string &url)
49 : {
50 52 : m_url = url;
51 52 : }
52 :
53 110 : const std::string &CmdInit::get_url() const
54 : {
55 110 : return m_url;
56 : }
57 :
58 18 : void CmdInit::set_branch(const std::string &branch)
59 : {
60 18 : m_branch = branch;
61 18 : }
62 :
63 34 : const std::string &CmdInit::get_branch() const
64 : {
65 34 : return m_branch;
66 : }
67 :
68 16 : void CmdInit::set_rev_set(const std::string &rev_set)
69 : {
70 16 : m_rev_set = rev_set;
71 16 : }
72 :
73 13 : const std::string &CmdInit::get_rev_set() const
74 : {
75 13 : return m_rev_set;
76 : }
77 :
78 18 : void CmdInit::set_prefer_branch(const std::string &prefer_branch)
79 : {
80 18 : m_prefer_branch = prefer_branch;
81 18 : }
82 :
83 43 : const std::string &CmdInit::get_prefer_branch() const
84 : {
85 43 : return m_prefer_branch;
86 : }
87 :
88 15 : void CmdInit::set_manifest_name(const std::string &manifest_name)
89 : {
90 15 : m_manifest_name = manifest_name;
91 15 : }
92 :
93 35 : const std::string &CmdInit::get_manifest_name() const
94 : {
95 35 : return m_manifest_name;
96 : }
97 :
98 16 : void CmdInit::set_project_name(const std::string &project_name)
99 : {
100 16 : m_project_name = project_name;
101 16 : }
102 :
103 3 : const std::string &CmdInit::get_project_name() const
104 : {
105 3 : return m_project_name;
106 : }
107 :
108 20 : void CmdInit::set_manifest_local(const std::string &manifest_local)
109 : {
110 20 : m_manifest_local = manifest_local;
111 20 : }
112 :
113 38 : const std::string &CmdInit::get_manifest_local() const
114 : {
115 38 : return m_manifest_local;
116 : }
117 :
118 17 : void CmdInit::set_google_manifest(bool google_manifest)
119 : {
120 17 : m_google_manifest = google_manifest;
121 17 : m_manifest_type = manifest::Type::GOOGLE_MANIFEST;
122 17 : }
123 :
124 33 : bool CmdInit::get_google_manifest() const
125 : {
126 33 : return m_google_manifest;
127 : }
128 :
129 15 : void CmdInit::set_git_super_project(bool git_super_project)
130 : {
131 15 : m_git_super_project = git_super_project;
132 15 : m_manifest_type = manifest::Type::GOOGLE_MANIFEST;
133 15 : }
134 :
135 56 : bool CmdInit::get_git_super_project() const
136 : {
137 56 : return m_git_super_project;
138 : }
139 :
140 16 : void CmdInit::set_parse_mode(manifest::ParseMode mode)
141 : {
142 16 : m_parse_mode = mode;
143 16 : }
144 :
145 42 : manifest::ParseMode CmdInit::get_parse_mode() const
146 : {
147 42 : return m_parse_mode;
148 : }
149 :
150 0 : std::string CmdInit::get_extra_start_msg()
151 : {
152 0 : if (!get_manifest_local().empty()) return "\n local : " + get_manifest_local();
153 0 : return "\n url : " + get_url();
154 : }
155 :
156 30 : Result CmdInit::impl_run()
157 : {
158 30 : Result result;
159 30 : ETRC_CALL_RET_NP(result);
160 :
161 30 : if (get_git_super_project()) warn("The option --git-super is not implemented yet");
162 :
163 30 : result = only_one_folder_or_workspace();
164 30 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
165 :
166 30 : result = load_esysrepo_folder();
167 30 : if (result.ok())
168 : {
169 1 : if (get_url().empty() && !get_project_name().empty())
170 : {
171 0 : info("Finding URL for project " + get_project_name() + " ...");
172 0 : result = find_url_from_project_name();
173 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
174 0 : if (!get_url().empty()) info("Found URL : '" + get_url() + "'.");
175 : }
176 2 : return ETRC_RET(load_esysrepo_folder_succeeded());
177 : }
178 58 : return ETRC_RET(load_esysrepo_folder_failed());
179 30 : }
180 :
181 1 : Result CmdInit::load_esysrepo_folder_succeeded()
182 : {
183 1 : Result result;
184 1 : ETRC_CALL_RET_NP(result);
185 :
186 1 : assert(get_config_folder() != nullptr);
187 2 : if (get_config_folder() == nullptr)
188 : {
189 0 : error("Internal error [CmdInit::load_esysrepo_folder_succeeded] get_config_folder() == nullptr");
190 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR));
191 : }
192 :
193 1 : auto config = get_config_folder()->get_config();
194 1 : assert(config != nullptr);
195 1 : if (config == nullptr)
196 : {
197 0 : error("Internal error [CmdInit::load_esysrepo_folder_succeeded] config == nullptr");
198 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR));
199 : }
200 :
201 1 : if (!get_manifest_local().empty())
202 : {
203 0 : boost::filesystem::path local_abs = boost::filesystem::absolute(get_manifest_local()).lexically_normal();
204 0 : boost::filesystem::path ws = boost::filesystem::absolute(get_workspace_path()).lexically_normal();
205 0 : std::string want = boost::filesystem::relative(local_abs, ws).generic_string();
206 0 : if (want.empty()) want = ".";
207 0 : if (want != config->get_manifest_local())
208 : {
209 0 : std::stringstream oss;
210 0 : oss << "Current manifest local is:" << std::endl;
211 0 : oss << " '" << config->get_manifest_local() << "'" << std::endl;
212 0 : oss << "But --manifest-local given is different:" << std::endl;
213 0 : oss << " '" << want << "'" << std::endl;
214 0 : oss << "Use --force if this is really wanted.";
215 0 : error(oss.str());
216 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMD_INCORRECT_PARAMETERS_IN_CONTEXT, oss.str()));
217 0 : }
218 0 : warn("Nothing to do: workspace already initialized from this local manifest.");
219 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
220 0 : }
221 :
222 1 : git::URL url(get_url());
223 1 : if (url != config->get_manifest_url())
224 : {
225 0 : std::stringstream oss;
226 :
227 0 : oss << "Current manifest url is:" << std::endl;
228 0 : oss << " '" << config->get_manifest_url() << "'" << std::endl;
229 0 : oss << "But manifest url given is different:" << std::endl;
230 0 : oss << " '" << get_url() << "'" << std::endl;
231 0 : oss << "Use --force if this is really wanted.";
232 0 : error(oss.str());
233 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMD_INCORRECT_PARAMETERS_IN_CONTEXT, oss.str()));
234 0 : }
235 :
236 1 : if (get_branch().empty())
237 : {
238 1 : warn("Nothing to do as a branch is not provided");
239 2 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
240 : }
241 :
242 0 : boost::filesystem::path manifest_git_path = get_config_folder()->get_manifest_repo_path();
243 :
244 0 : auto git_helper = new_git_helper();
245 :
246 0 : debug(1, "Test debug, before git_helper");
247 0 : git_helper->debug(1, "Test debug with git_helper");
248 :
249 0 : result = git_helper->open(manifest_git_path.lexically_normal().make_preferred().string(), log::DEBUG);
250 0 : if (result.error())
251 : {
252 0 : std::string err_str = "Opening git repo holding the manifest failed.";
253 0 : error(err_str);
254 0 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
255 0 : }
256 :
257 0 : result = get_git()->checkout(get_branch() /*, get_force()*/);
258 0 : if (result.error())
259 : {
260 0 : std::string err_str = "Checkout branch failed.";
261 0 : error(err_str);
262 0 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
263 0 : }
264 :
265 0 : result = get_git()->close();
266 :
267 0 : return ETRC_RET(ESYSREPO_RESULT(result));
268 2 : }
269 :
270 29 : Result CmdInit::load_esysrepo_folder_failed()
271 : {
272 29 : Result result;
273 29 : ETRC_CALL_RET_NP(result);
274 :
275 : // Since it failed, either there is no .esysrepo folder or the xml config
276 : // file is corrupted
277 29 : result = create_esysrepo_folder();
278 29 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
279 :
280 : // Now there are 2 cases, we couldn't load be the ESysRepo config folder because
281 : // it got corrupted, or it's not there at all.
282 29 : result = fetch_manifest();
283 30 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
284 :
285 28 : return ETRC_RET(Result::OK);
286 29 : }
287 :
288 29 : Result CmdInit::fetch_manifest()
289 : {
290 29 : Result result;
291 29 : ETRC_CALL_RET_NP(result);
292 :
293 33 : if (!get_manifest_local().empty()) return ETRC_RET(fetch_local_manifest());
294 :
295 25 : if (get_google_manifest())
296 4 : return ETRC_RET(fetch_google_manifest());
297 23 : else if (get_git_super_project())
298 0 : return ETRC_RET(fetch_git_super_project());
299 :
300 : // Here we can't be sure, either it's a google manifest, a esysrepo manifest or a git super project
301 46 : return ETRC_RET(fetch_unknown_manifest());
302 29 : }
303 :
304 32 : std::string CmdInit::get_google_manifest_filename() const
305 : {
306 32 : if (!get_manifest_name().empty()) return get_manifest_name();
307 32 : return "default.xml";
308 : }
309 :
310 25 : Result CmdInit::clone_manifest_repo(GitHelper &git_helper, std::string &git_repo_path)
311 : {
312 25 : Result result;
313 25 : ETRC_CALL_RET_NP(result);
314 :
315 50 : if (get_git() == nullptr) return ETRC_RET(ESYSREPO_RESULT(ResultCode::INTERNAL_ERROR));
316 :
317 25 : if (get_url().empty() && !get_project_name().empty())
318 : {
319 2 : info("Finding URL for project " + get_project_name() + " ...");
320 1 : result = find_url_from_project_name();
321 1 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
322 2 : if (!get_url().empty()) info("Found URL : '" + get_url() + "'.");
323 : }
324 :
325 25 : if (get_url().empty())
326 : {
327 0 : error("URL is empty");
328 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMDINIT_NO_URL));
329 : }
330 :
331 25 : boost::filesystem::path path = get_config_folder()->get_temp_path();
332 25 : path /= "unknown_manifest";
333 50 : git_repo_path = path.lexically_normal().make_preferred().string();
334 :
335 25 : debug(1, "Test debug, before git_helper");
336 25 : git_helper.debug(1, "Test debug with git_helper");
337 :
338 25 : result = git_helper.clone(get_url(), git_repo_path, false, log::DEBUG);
339 25 : if (result.error())
340 : {
341 0 : error("Cloning failed.");
342 0 : return ETRC_RET(ESYSREPO_RESULT(result));
343 : }
344 :
345 25 : if (!get_branch().empty())
346 : {
347 2 : result = get_git()->checkout(get_branch() /*, get_force()*/);
348 2 : if (result.error())
349 : {
350 0 : std::string err_str = "Cloning failed : checkout branch failed.";
351 0 : error(err_str);
352 0 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
353 0 : }
354 : }
355 :
356 25 : result = git_helper.close(log::DEBUG);
357 25 : if (result.error())
358 : {
359 0 : std::string err_str = "Cloning failed : can't clod the git repo.";
360 0 : error(err_str);
361 0 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
362 0 : }
363 :
364 50 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
365 25 : }
366 :
367 16 : Result CmdInit::install_google_manifest(GitHelper &git_helper, const std::string &git_repo_path)
368 : {
369 16 : Result result;
370 16 : ETRC_CALL_RET(result, git_repo_path);
371 :
372 16 : auto config = get_config_folder()->get_or_new_config();
373 16 : config->set_manifest_type(manifest::Type::GOOGLE_MANIFEST);
374 16 : config->set_manifest_format(manifest::Format::XML);
375 16 : config->set_manifest_url(get_url());
376 16 : config->set_parse_mode(get_parse_mode());
377 16 : if (!get_prefer_branch().empty()) config->set_prefer_branch(get_prefer_branch());
378 :
379 16 : boost::filesystem::path source = git_repo_path;
380 32 : boost::filesystem::path rel_source = boost::filesystem::relative(source);
381 16 : boost::filesystem::path target = get_config_folder()->get_path();
382 32 : boost::filesystem::path rel_target = boost::filesystem::relative(target);
383 16 : target /= grepo::Manifest::get_folder_name();
384 32 : rel_target = boost::filesystem::relative(target);
385 :
386 16 : bool result_bool = boost::filesystem::create_directory(target);
387 16 : if (!result_bool)
388 : {
389 0 : std::string err_str = "Couldn't create the folder : " + rel_target.string();
390 0 : error(err_str);
391 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FOLDER_CREATION_ERROR, err_str));
392 0 : }
393 16 : debug(0, "Created folder : " + rel_target.string());
394 :
395 16 : result = git_helper.move(source.string(), target.string(), true, log::Level::DEBUG);
396 16 : if (result == ResultCode::FAILED_TO_COPY) return ETRC_RET(ESYSREPO_RESULT(result));
397 16 : if (result == ResultCode::FAILED_TO_REMOVE_ALL)
398 0 : warn("While moving folder " + rel_source.string() + " some files were left behind.");
399 :
400 16 : std::string manifest_path = "grepo/" + get_google_manifest_filename();
401 32 : get_config_folder()->get_config()->set_manifest_path(manifest_path);
402 48 : return ETRC_RET(get_config_folder()->write_config_file());
403 32 : }
404 :
405 2 : Result CmdInit::fetch_google_manifest()
406 : {
407 2 : Result result;
408 2 : ETRC_CALL_RET_NP(result);
409 :
410 2 : info("Using Google repo tool manifest (--google).");
411 :
412 2 : auto git_helper = new_git_helper();
413 2 : std::string git_repo_path;
414 :
415 2 : result = clone_manifest_repo(*git_helper, git_repo_path);
416 2 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
417 :
418 2 : boost::filesystem::path file_path = git_repo_path;
419 4 : file_path /= get_google_manifest_filename();
420 :
421 2 : if (!boost::filesystem::exists(file_path))
422 : {
423 0 : std::string err_str = "Google repo manifest file not found: " + get_google_manifest_filename();
424 0 : error(err_str);
425 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FILE_NOT_EXISTING, err_str));
426 0 : }
427 :
428 2 : result = install_google_manifest(*git_helper, git_repo_path);
429 4 : return ETRC_RET(ESYSREPO_RESULT(result));
430 4 : }
431 :
432 0 : Result CmdInit::read_esysrepo_manifest(std::shared_ptr<Manifest> manifest, const std::string &filename)
433 : {
434 0 : Result result;
435 0 : ETRC_CALL_RET_NP(result);
436 :
437 0 : manifest::File manifest_file;
438 :
439 0 : manifest_file.set_data(manifest);
440 0 : manifest_file.set_parse_mode(get_parse_mode());
441 0 : result = manifest_file.read(filename);
442 0 : return ETRC_RET(ESYSREPO_RESULT(result));
443 0 : }
444 :
445 12 : Result CmdInit::read_esysrepo_manifest_xml(std::shared_ptr<Manifest> manifest, const std::string &filename)
446 : {
447 12 : Result result;
448 12 : ETRC_CALL_RET_NP(result);
449 :
450 12 : manifest::XMLFile manifest_file;
451 :
452 24 : manifest_file.set_data(manifest);
453 12 : manifest_file.set_parse_mode(get_parse_mode());
454 12 : result = manifest_file.read(filename);
455 36 : return ETRC_RET(ESYSREPO_RESULT(result));
456 12 : }
457 :
458 0 : Result CmdInit::read_esysrepo_manifest_json(std::shared_ptr<Manifest> manifest, const std::string &filename)
459 : {
460 : //! \TODO
461 0 : return ESYSREPO_RESULT(ResultCode::NOT_IMPLEMENTED);
462 : }
463 :
464 9 : Result CmdInit::fetch_esysrepo_manifest(GitHelper &git_helper, const std::string &git_repo_path,
465 : const std::string &manifest_filename)
466 : {
467 9 : Result result;
468 9 : ETRC_CALL_RET(result, git_repo_path, manifest_filename);
469 :
470 9 : std::string manifest_path;
471 9 : boost::filesystem::path manifest_filename_path = manifest_filename;
472 9 : std::string manifest_filename_ext = manifest_filename_path.extension().string();
473 :
474 9 : info("ESysRepo manifest detected.");
475 9 : debug(0, "Manifest filename : " + manifest_filename);
476 :
477 9 : auto config_file = get_config_folder()->get_or_new_config();
478 :
479 9 : config_file->set_manifest_type(manifest::Type::ESYSREPO_MANIFEST);
480 9 : config_file->set_manifest_url(get_url());
481 9 : config_file->set_parse_mode(get_parse_mode());
482 9 : if (!get_prefer_branch().empty()) config_file->set_prefer_branch(get_prefer_branch());
483 :
484 9 : boost::filesystem::path source = git_repo_path;
485 18 : boost::filesystem::path rel_source = boost::filesystem::relative(source);
486 9 : boost::filesystem::path target;
487 9 : boost::filesystem::path file_path = git_repo_path;
488 9 : file_path /= manifest_filename;
489 :
490 9 : std::shared_ptr<Manifest> manifest = std::make_shared<Manifest>();
491 :
492 9 : if (manifest_filename_ext == ".manifest")
493 : {
494 0 : result = read_esysrepo_manifest(manifest, file_path.string());
495 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
496 : }
497 9 : else if (manifest_filename_ext == ".xml")
498 : {
499 18 : result = read_esysrepo_manifest_xml(manifest, file_path.string());
500 9 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
501 9 : manifest->set_format(manifest::Format::XML);
502 9 : config_file->set_manifest_format(manifest::Format::XML);
503 : }
504 0 : else if (manifest_filename_ext == ".json")
505 : {
506 0 : result = read_esysrepo_manifest_json(manifest, file_path.string());
507 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
508 0 : manifest->set_format(manifest::Format::JSON);
509 0 : config_file->set_manifest_format(manifest::Format::JSON);
510 : }
511 : else
512 : {
513 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::MANIFEST_FILE_EXT_UNKNOWN, manifest_filename_ext));
514 : }
515 :
516 9 : config_file->set_manifest_format(manifest->get_format());
517 :
518 9 : if (manifest->get_kind() == manifest::Kind::NOT_SET) manifest->set_kind(manifest::Kind::ISOLATED);
519 :
520 9 : if (manifest->get_kind() == manifest::Kind::EMBEDDED)
521 : {
522 1 : info("Embedded kind.");
523 1 : config_file->set_manifest_kind(manifest::Kind::EMBEDDED);
524 2 : target = get_config_folder()->get_workspace_path();
525 1 : result = git_helper.move(source.string(), target.string(), true, log::Level::DEBUG);
526 1 : if (result == ResultCode::FAILED_TO_COPY) return ETRC_RET(ESYSREPO_RESULT(result));
527 1 : if (result == ResultCode::FAILED_TO_REMOVE_ALL)
528 0 : warn("While moving folder " + rel_source.string() + " some files were left behind.");
529 1 : target = "..";
530 1 : target /= manifest_filename;
531 1 : manifest_path = target.string();
532 : }
533 8 : else if (manifest->get_kind() == manifest::Kind::ISOLATED)
534 : {
535 8 : info("Isolated kind.");
536 8 : config_file->set_manifest_kind(manifest::Kind::ISOLATED);
537 16 : target = get_config_folder()->get_path();
538 :
539 8 : target /= manifest::Base::get_folder_name();
540 8 : boost::filesystem::path rel_target;
541 16 : rel_target = boost::filesystem::relative(target);
542 :
543 8 : bool result_bool = boost::filesystem::create_directory(target);
544 8 : if (!result_bool)
545 : {
546 0 : error("Couldn't create the folder : " + rel_target.string());
547 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FOLDER_CREATION_ERROR, target.string()));
548 : }
549 : else
550 8 : debug(0, "Created folder : " + rel_target.string());
551 :
552 8 : result = git_helper.move(source.string(), target.string(), true, log::Level::DEBUG);
553 8 : if (result == ResultCode::FAILED_TO_COPY) return ETRC_RET(ESYSREPO_RESULT(result));
554 8 : if (result == ResultCode::FAILED_TO_REMOVE_ALL)
555 0 : warn("While moving folder " + rel_source.string() + " some files were left behind.");
556 8 : target = manifest::Base::get_folder_name();
557 8 : target /= manifest_filename;
558 24 : manifest_path = target.generic_path().string();
559 8 : }
560 : else
561 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::MANIFEST_KIND_UNKNOWN));
562 :
563 9 : config_file->set_manifest_path(manifest_path);
564 :
565 9 : if (!get_rev_set().empty())
566 : {
567 0 : result = manifest->select_revision_set(get_rev_set());
568 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
569 0 : config_file->set_revision_set(get_rev_set());
570 : }
571 9 : if (!get_prefer_branch().empty()) config_file->set_prefer_branch(get_prefer_branch());
572 :
573 9 : result = get_config_folder()->write_config_file();
574 18 : return ETRC_RET(ESYSREPO_RESULT(result));
575 18 : }
576 :
577 0 : Result CmdInit::fetch_git_super_project()
578 : {
579 0 : return ESYSREPO_RESULT(ResultCode::NOT_IMPLEMENTED);
580 : }
581 :
582 : namespace
583 : {
584 :
585 13 : boost::filesystem::path abs_norm(const boost::filesystem::path &path)
586 : {
587 13 : return boost::filesystem::absolute(path).lexically_normal();
588 : }
589 :
590 3 : bool paths_same(const boost::filesystem::path &left, const boost::filesystem::path &right)
591 : {
592 3 : boost::system::error_code ec;
593 3 : if (boost::filesystem::exists(left) && boost::filesystem::exists(right))
594 : {
595 3 : bool same = boost::filesystem::equivalent(left, right, ec);
596 3 : if (!ec) return same;
597 : }
598 0 : return abs_norm(left) == abs_norm(right);
599 : }
600 :
601 3 : std::string rel_generic(const boost::filesystem::path &from_dir, const boost::filesystem::path &target,
602 : const std::string &fallback)
603 : {
604 3 : boost::system::error_code ec;
605 6 : auto rel = boost::filesystem::relative(abs_norm(target), abs_norm(from_dir), ec);
606 3 : if (ec) return fallback.empty() ? "." : fallback;
607 3 : std::string text = rel.generic_string();
608 3 : if (text.empty()) return ".";
609 6 : return text;
610 3 : }
611 :
612 2 : bool is_native_manifest_filename(const boost::filesystem::path &file)
613 : {
614 2 : const std::string name = file.filename().string();
615 4 : return name == ".esysrepo.manifest" || name == ".esysrepo.manifest.xml" || name == ".esysrepo.manifest.json";
616 2 : }
617 :
618 : } // namespace
619 :
620 4 : Result CmdInit::fetch_local_manifest()
621 : {
622 4 : Result result;
623 4 : ETRC_CALL_RET_NP(result);
624 :
625 4 : if (!get_branch().empty())
626 : {
627 0 : std::string err_str = "--manifest-branch / -b cannot be combined with --manifest-local / -l";
628 0 : error(err_str);
629 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMD_INCORRECT_PARAMETERS_COMBINATION, err_str));
630 0 : }
631 :
632 4 : boost::filesystem::path local_path = abs_norm(get_manifest_local());
633 4 : if (!boost::filesystem::exists(local_path))
634 : {
635 1 : std::string err_str = "Local manifest path does not exist: " + local_path.string();
636 1 : error(err_str);
637 2 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::PATH_NOT_EXISTING, err_str));
638 1 : }
639 :
640 3 : const bool is_file = boost::filesystem::is_regular_file(local_path);
641 3 : const bool is_dir = boost::filesystem::is_directory(local_path);
642 3 : if (!is_file && !is_dir)
643 : {
644 0 : std::string err_str = "Local manifest path is not a file or directory: " + local_path.string();
645 0 : error(err_str);
646 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::PATH_NOT_EXISTING, err_str));
647 0 : }
648 :
649 3 : if (is_file && !get_manifest_name().empty())
650 : {
651 0 : std::string err_str = "--manifest-name / -m cannot be combined with --manifest-local pointing at a file";
652 0 : error(err_str);
653 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMD_INCORRECT_PARAMETERS_COMBINATION, err_str));
654 0 : }
655 :
656 3 : boost::filesystem::path source_file;
657 3 : bool google_manifest = get_google_manifest();
658 3 : bool git_super = false;
659 :
660 3 : if (is_file)
661 : {
662 2 : source_file = local_path;
663 2 : if (!google_manifest) google_manifest = !is_native_manifest_filename(source_file) && source_file.extension() == ".xml";
664 : }
665 : else
666 : {
667 1 : if (get_google_manifest() || !get_manifest_name().empty())
668 : {
669 0 : source_file = local_path / (get_manifest_name().empty() ? get_google_manifest_filename() : get_manifest_name());
670 0 : if (!boost::filesystem::exists(source_file))
671 : {
672 0 : std::string err_str = "Manifest file not found in folder: " + source_file.string();
673 0 : error(err_str);
674 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FILE_NOT_EXISTING, err_str));
675 0 : }
676 0 : google_manifest = get_google_manifest() || !is_native_manifest_filename(source_file);
677 : }
678 : else
679 : {
680 1 : const char *native_names[] = {".esysrepo.manifest", ".esysrepo.manifest.xml", ".esysrepo.manifest.json"};
681 2 : for (const char *name : native_names)
682 : {
683 4 : boost::filesystem::path candidate = local_path / name;
684 2 : if (boost::filesystem::exists(candidate))
685 : {
686 1 : source_file = candidate;
687 1 : google_manifest = false;
688 1 : break;
689 : }
690 2 : }
691 1 : if (source_file.empty())
692 : {
693 0 : boost::filesystem::path candidate = local_path / get_google_manifest_filename();
694 0 : if (boost::filesystem::exists(candidate))
695 : {
696 0 : source_file = candidate;
697 : google_manifest = true;
698 : }
699 0 : }
700 1 : if (source_file.empty())
701 : {
702 0 : boost::filesystem::path candidate = local_path / ".gitmodules";
703 0 : if (boost::filesystem::exists(candidate))
704 : {
705 0 : source_file = candidate;
706 : git_super = true;
707 : }
708 0 : }
709 1 : if (source_file.empty())
710 : {
711 0 : std::string err_str = "No manifest found in folder: " + local_path.string();
712 0 : error(err_str);
713 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMDINIT_NO_LOCAL_MANIFEST, err_str));
714 0 : }
715 : }
716 : }
717 :
718 3 : info("Using local manifest (no clone).");
719 3 : debug(0, "Local source : " + local_path.string());
720 3 : debug(0, "Manifest file : " + source_file.string());
721 :
722 3 : auto config = get_config_folder()->get_or_new_config();
723 3 : config->set_parse_mode(get_parse_mode());
724 3 : if (!get_prefer_branch().empty()) config->set_prefer_branch(get_prefer_branch());
725 6 : config->set_manifest_local(rel_generic(get_workspace_path(), local_path, get_manifest_local()));
726 : // No git remote for -l
727 3 : config->set_manifest_url("");
728 :
729 3 : boost::filesystem::path workspace = abs_norm(get_workspace_path());
730 3 : boost::filesystem::path source_dir = is_file ? local_path.parent_path() : local_path;
731 3 : const bool source_is_workspace = paths_same(source_dir, workspace);
732 :
733 3 : if (git_super)
734 : {
735 0 : info("Git submodule detected.");
736 0 : config->set_manifest_type(manifest::Type::GIT_SUPER_PROJECT);
737 0 : result = get_config_folder()->write_config_file();
738 0 : return ETRC_RET(ESYSREPO_RESULT(result));
739 : }
740 :
741 3 : if (google_manifest)
742 : {
743 0 : info("Google repo tool manifest detected.");
744 0 : config->set_manifest_type(manifest::Type::GOOGLE_MANIFEST);
745 0 : config->set_manifest_format(manifest::Format::XML);
746 0 : config->set_manifest_kind(manifest::Kind::ISOLATED);
747 :
748 0 : boost::filesystem::path dest_dir = get_config_folder()->get_path();
749 0 : dest_dir /= grepo::Manifest::get_folder_name();
750 0 : boost::system::error_code ec;
751 0 : boost::filesystem::create_directories(dest_dir, ec);
752 0 : if (ec)
753 : {
754 0 : error("Couldn't create the folder : " + dest_dir.string());
755 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FOLDER_CREATION_ERROR, dest_dir.string()));
756 : }
757 0 : boost::filesystem::path dest_file = dest_dir / source_file.filename();
758 0 : boost::filesystem::copy_file(source_file, dest_file, boost::filesystem::copy_options::overwrite_existing, ec);
759 0 : if (ec)
760 : {
761 0 : error("Couldn't copy the manifest file : " + source_file.string());
762 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_COPY, source_file.string()));
763 : }
764 0 : config->set_manifest_path((boost::filesystem::path(grepo::Manifest::get_folder_name()) / source_file.filename())
765 : .generic_string());
766 0 : result = get_config_folder()->write_config_file();
767 0 : return ETRC_RET(ESYSREPO_RESULT(result));
768 0 : }
769 :
770 3 : info("ESysRepo manifest detected.");
771 3 : config->set_manifest_type(manifest::Type::ESYSREPO_MANIFEST);
772 :
773 3 : auto manifest = std::make_shared<Manifest>();
774 3 : std::string ext = source_file.extension().string();
775 12 : if (ext == ".manifest" || source_file.filename() == ".esysrepo.manifest")
776 : {
777 0 : result = read_esysrepo_manifest(manifest, source_file.string());
778 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
779 : }
780 3 : else if (ext == ".xml")
781 : {
782 6 : result = read_esysrepo_manifest_xml(manifest, source_file.string());
783 3 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
784 3 : manifest->set_format(manifest::Format::XML);
785 : }
786 0 : else if (ext == ".json")
787 : {
788 0 : result = read_esysrepo_manifest_json(manifest, source_file.string());
789 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
790 0 : manifest->set_format(manifest::Format::JSON);
791 : }
792 : else
793 : {
794 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::MANIFEST_FILE_EXT_UNKNOWN, ext));
795 : }
796 :
797 3 : if (manifest->get_kind() == manifest::Kind::NOT_SET) manifest->set_kind(manifest::Kind::ISOLATED);
798 3 : config->set_manifest_format(manifest->get_format());
799 :
800 3 : const bool leave_in_place = (manifest->get_kind() == manifest::Kind::EMBEDDED) && source_is_workspace;
801 3 : if (leave_in_place)
802 : {
803 1 : info("Embedded kind.");
804 1 : config->set_manifest_kind(manifest::Kind::EMBEDDED);
805 1 : boost::filesystem::path stored = "..";
806 2 : stored /= source_file.filename();
807 1 : config->set_manifest_path(stored.generic_string());
808 1 : }
809 : else
810 : {
811 4 : info(manifest->get_kind() == manifest::Kind::EMBEDDED ? "Embedded kind (source outside workspace, copying)."
812 : : "Isolated kind.");
813 2 : config->set_manifest_kind(manifest::Kind::ISOLATED);
814 2 : boost::filesystem::path dest_dir = get_config_folder()->get_path();
815 2 : dest_dir /= manifest::Base::get_folder_name();
816 2 : boost::system::error_code ec;
817 2 : boost::filesystem::create_directories(dest_dir, ec);
818 2 : if (ec)
819 : {
820 0 : error("Couldn't create the folder : " + dest_dir.string());
821 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FOLDER_CREATION_ERROR, dest_dir.string()));
822 : }
823 4 : boost::filesystem::path dest_file = dest_dir / source_file.filename();
824 2 : boost::filesystem::copy_file(source_file, dest_file, boost::filesystem::copy_options::overwrite_existing, ec);
825 2 : if (ec)
826 : {
827 0 : error("Couldn't copy the manifest file : " + source_file.string());
828 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_COPY, source_file.string()));
829 : }
830 4 : config->set_manifest_path(
831 6 : (boost::filesystem::path(manifest::Base::get_folder_name()) / source_file.filename()).generic_string());
832 2 : }
833 :
834 3 : if (!get_rev_set().empty())
835 : {
836 0 : result = manifest->select_revision_set(get_rev_set());
837 0 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
838 0 : config->set_revision_set(get_rev_set());
839 : }
840 3 : if (!get_prefer_branch().empty()) config->set_prefer_branch(get_prefer_branch());
841 :
842 3 : result = get_config_folder()->write_config_file();
843 6 : return ETRC_RET(ESYSREPO_RESULT(result));
844 17 : }
845 :
846 23 : Result CmdInit::fetch_unknown_manifest()
847 : {
848 23 : Result result;
849 23 : ETRC_CALL_RET_NP(result);
850 :
851 23 : info("Detecting the manifest type ...");
852 :
853 23 : auto git_helper = new_git_helper();
854 23 : std::string git_repo_path;
855 :
856 23 : result = clone_manifest_repo(*git_helper, git_repo_path);
857 23 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
858 :
859 23 : boost::filesystem::path path = git_repo_path;
860 23 : boost::filesystem::path file_path = path;
861 23 : file_path /= ".esysrepo.manifest";
862 :
863 23 : if (boost::filesystem::exists(file_path))
864 : {
865 0 : result = fetch_esysrepo_manifest(*git_helper.get(), path.string(), ".esysrepo.manifest");
866 0 : return ETRC_RET(ESYSREPO_RESULT(result));
867 : }
868 :
869 23 : file_path = path;
870 23 : file_path /= ".esysrepo.manifest.xml";
871 :
872 23 : if (boost::filesystem::exists(file_path))
873 : {
874 9 : result = fetch_esysrepo_manifest(*git_helper.get(), path.string(), ".esysrepo.manifest.xml");
875 18 : return ETRC_RET(ESYSREPO_RESULT(result));
876 : }
877 :
878 14 : file_path = path;
879 14 : file_path /= ".esysrepo.manifest.json";
880 :
881 14 : if (boost::filesystem::exists(file_path))
882 : {
883 0 : result = fetch_esysrepo_manifest(*git_helper.get(), path.string(), ".esysrepo.manifest.json");
884 0 : return ETRC_RET(ESYSREPO_RESULT(result));
885 : }
886 :
887 14 : file_path = path;
888 28 : file_path /= get_google_manifest_filename();
889 :
890 14 : if (boost::filesystem::exists(file_path))
891 : {
892 14 : info("Google repo tool manifest detected.");
893 14 : result = install_google_manifest(*git_helper, git_repo_path);
894 28 : return ETRC_RET(ESYSREPO_RESULT(result));
895 : }
896 :
897 0 : file_path = path;
898 0 : file_path /= ".gitmodules";
899 :
900 0 : if (boost::filesystem::exists(file_path))
901 : {
902 0 : info("Git submodule detected.");
903 0 : auto config = get_config_folder()->get_or_new_config();
904 :
905 0 : config->set_manifest_type(manifest::Type::GIT_SUPER_PROJECT);
906 0 : config->set_manifest_url(get_url());
907 0 : if (!get_prefer_branch().empty()) config->set_prefer_branch(get_prefer_branch());
908 0 : boost::filesystem::path source = path.string();
909 0 : boost::filesystem::path target = get_config_folder()->get_workspace_path();
910 :
911 0 : result = git_helper->move(source.string(), target.string(), true, log::Level::DEBUG);
912 0 : if (result == ResultCode::FAILED_TO_COPY) return ETRC_RET(ESYSREPO_RESULT(result));
913 0 : if (result == ResultCode::FAILED_TO_REMOVE_ALL)
914 0 : warn("While moving folder " + source.string() + " some files were left behind.");
915 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
916 0 : }
917 :
918 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::CMDINIT_FAILED_FETCH_UNKNOWN_MANIFEST));
919 69 : }
920 :
921 30 : Result CmdInit::load_esysrepo_folder()
922 : {
923 30 : Result result;
924 30 : ETRC_CALL_RET_NP(result);
925 :
926 30 : auto config_folder = std::make_shared<ConfigFolder>();
927 30 : config_folder->set_logger_if(get_logger_if());
928 :
929 60 : set_config_folder(config_folder);
930 :
931 30 : ETRC_MSG("parent_path = " + get_workspace_path());
932 :
933 30 : result = config_folder->open(get_workspace_path(), false);
934 90 : return ETRC_RET(ESYSREPO_RESULT(result));
935 30 : }
936 :
937 29 : Result CmdInit::create_esysrepo_folder()
938 : {
939 29 : Result result;
940 29 : ETRC_CALL_RET_NP(result);
941 :
942 29 : auto config_folder = get_config_folder();
943 29 : if (config_folder == nullptr)
944 : {
945 0 : config_folder = std::make_shared<ConfigFolder>();
946 0 : config_folder->set_logger_if(get_logger_if());
947 :
948 0 : set_config_folder(config_folder);
949 : }
950 :
951 29 : ETRC_MSG("parent_path = " + get_workspace_path());
952 :
953 29 : result = config_folder->create(get_workspace_path(), true);
954 87 : return ESYSREPO_RESULT(result);
955 29 : }
956 :
957 1 : Result CmdInit::find_url_from_project_name()
958 : {
959 1 : Result result;
960 1 : ETRC_CALL_RET_NP(result);
961 :
962 1 : manifest::DirectoryMngr mngr;
963 :
964 1 : boost::filesystem::path path = get_config_folder()->get_temp_path();
965 1 : path /= "manifest_directories";
966 1 : mngr.set_folder_path(path.string());
967 :
968 1 : if (get_directories() == nullptr)
969 : {
970 1 : auto directories = std::make_shared<manifest::Directories>();
971 1 : directories->add_default_libesys();
972 :
973 2 : mngr.set_directories(directories);
974 3 : set_directories(directories);
975 1 : }
976 :
977 1 : result = mngr.load();
978 1 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
979 :
980 1 : auto project = get_directories()->find_project(get_project_name());
981 1 : if (project == nullptr) return ETRC_RET(ESYSREPO_RESULT(ResultCode::GENERIC_ERROR));
982 :
983 1 : set_url(project->get_url_ssh());
984 2 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
985 1 : }
986 :
987 1 : void CmdInit::set_directories(std::shared_ptr<manifest::Directories> directories)
988 : {
989 1 : m_directories = directories;
990 1 : }
991 :
992 2 : std::shared_ptr<manifest::Directories> CmdInit::get_directories() const
993 : {
994 2 : return m_directories;
995 : }
996 :
997 : } // namespace esys::repo::exe
|