src/esys/repo/exe/cmd.cpp

1 error line(s), 1 error(s)

1/*!
2 * \file esys/repo/exe/cmd.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/cmd.h"
20#include "esys/repo/filesystem.h"
21#include "esys/repo/configfolder.h"
22#include "esys/repo/gitmngr.h"
23#include "esys/repo/manifest/capture.h"
24#include "esys/repo/manifest/repository.h"
25#include "esys/repo/manifest/multifilexml.h"
26
27#include <esys/log/consolelockguard.h>
28
29#include <boost/filesystem.hpp>
30
31#include <iomanip>
32#include <iostream>
33#include <sstream>
34
35namespace esys::repo::exe
36{
37
38Cmd::Cmd(const std::string &name)
39 : log::User()
40 , m_name(name)
41{
42}
43
44Cmd::~Cmd() = default;
45
46const std::string &Cmd::get_name() const
47{
48 return m_name;
49}
50
51void Cmd::set_job_count(int job_count)
52{
53 m_job_count = job_count;
54}
55
56int Cmd::get_job_count() const
57{
58 return m_job_count;
59}
60
61void Cmd::set_user_config(std::shared_ptr<UserConfig> user_config)
62{
63 m_user_config = user_config;
64}
65
66std::shared_ptr<UserConfig> Cmd::get_user_config() const
67{
68 return m_user_config;
69}
70
71void Cmd::set_user_folder(std::shared_ptr<UserFolder> user_folder)
72{
73 m_user_folder = user_folder;
74}
75
76std::shared_ptr<UserFolder> Cmd::get_user_folder() const
77{
78 return m_user_folder;
79}
80
81Result Cmd::load_or_create_user_config()
82{
83 if (get_user_folder() == nullptr) set_user_folder(std::make_shared<UserFolder>());
84
85 auto user_folder = get_user_folder();
86 auto result = user_folder->read();
87 if (result.error())
88 {
89 if (!boost::filesystem::exists(user_folder->get_user_config_file_path()))
90 {
91 result = user_folder->create(true);
92 if (result.error())
93 warn("User config file doesn't exit and couldn't create it.");
94 else
95 info("User config file created.");
96 }
97 else
98 warn("Couldn't read the user config file.");
99 }
100 else
101 info("User config file loaded successfully.");
102
103 return ESYSREPO_RESULT(ResultCode::OK);
104}
105
106Result Cmd::run()
107{
108 m_start_time = std::chrono::steady_clock::now();
109
110 if (get_print_cmd_name_by_base()) print_cmd_name();
111
112 if (get_git() == nullptr) set_git(GitMngr::new_ptr());
113 if (get_logger_if() != nullptr) get_git()->set_logger_if(get_logger_if());
114
115 Result result = load_or_create_user_config();
116 if (result.error())
117 {
118 error("Some features will not work because the user config file issues.");
119 }
120 result = impl_run();
121
122 auto stop_time = std::chrono::steady_clock::now();
123 auto d_milli = std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - m_start_time).count();
124
125 if (get_print_result_summary())
126 {
127 std::ostringstream oss;
128 std::string msg;
129
130 msg = get_name();
131 oss << " elapsed time (s): " << (d_milli / THOUSAND) << "." << (d_milli % THOUSAND);
132 if (result.ok())
133 {
134 msg += " done.\n";
135 msg += oss.str();
136 info(msg);
137 }
138 else
139 {
140 msg += " failed.\n";
141 msg += oss.str();
142 error(msg);
143 }
144 }
145 return ESYSREPO_RESULT(result);
146}
147
148void Cmd::set_manifest(std::shared_ptr<Manifest> manifest)
149{
150 m_manifest = manifest;
151}
152
153std::shared_ptr<Manifest> Cmd::get_manifest() const
154{
155 return m_manifest;
156}
157
158void Cmd::set_git(std::shared_ptr<GitBase> git)
159{
160 m_git = git;
161}
162
163std::shared_ptr<GitBase> Cmd::get_git() const
164{
165 return m_git;
166}
167
168void Cmd::set_workspace_path(const std::string &workspace_path)
169{
170 m_workspace_path = workspace_path;
171}
172
173const std::string &Cmd::get_workspace_path() const
174{
175 if ((get_config_folder() != nullptr) && (!get_config_folder()->get_workspace_path().empty()))
176 return get_config_folder()->get_workspace_path();
177 return m_workspace_path;
178}
179
180std::string Cmd::find_workspace_path(const std::string &path)
181{
182 boost::filesystem::path cur_path;
183
184 if (!path.empty())
185 cur_path = path;
186 else
187 cur_path = boost::filesystem::current_path();
188
189 while (!cur_path.empty())
190 {
191 if (ConfigFolder::is_config_folder(cur_path.string())) return cur_path.string();
192 cur_path = cur_path.parent_path();
193 }
194
195 return "";
196}
197
198std::string Cmd::find_git_repo_path(const std::string &path)
199{
200 boost::filesystem::path cur_path;
201
202 if (!path.empty())
203 cur_path = path;
204 else
205 cur_path = boost::filesystem::current_path();
206
207 while (!cur_path.empty())
208 {
209 if (GitBase::is_repo(cur_path.string())) return cur_path.string();
210 cur_path = cur_path.parent_path();
211 }
212
213 return "";
214}
215
216void Cmd::set_debug(bool debug)
217{
218 m_debug = debug;
219}
220
221bool Cmd::get_debug() const
222{
223 return m_debug;
224}
225
226void Cmd::set_folder(const std::string &folder)
227{
228 m_folder = folder;
229
230 /*boost::filesystem::path path = Cmd::find_workspace_path(folder);
231 if (path.empty()) return -1;
232
233 set_workspace_path(path.string());*/
234}
235
236const std::string &Cmd::get_folder() const
237{
238 return m_folder;
239}
240
241bool Cmd::is_git_root_folder(const std::string &path)
242{
243 boost::filesystem::path folder_path = path;
244
245 folder_path /= ".git";
246 return boost::filesystem::exists(folder_path);
247}
248
249std::string Cmd::find_git_root_path(const std::string &folder_path)
250{
251 boost::filesystem::path cur_path = folder_path;
252
253 while (!cur_path.empty())
254 {
255 if (is_git_root_folder(cur_path.string()))
256 {
257 return cur_path.string();
258 }
259 cur_path = cur_path.parent_path();
260 }
261 return "";
262}
263
264Result Cmd::load_capture(std::shared_ptr<manifest::Capture> capture, const std::string &note_text)
265{
266 manifest::MultiFileXML file_xml;
267 std::istringstream iss;
268
269 iss.str(note_text);
270 file_xml.set_capture(capture);
271
272 auto result = file_xml.read(iss);
273 if (result.error()) return ESYSREPO_RESULT(result);
274 return ESYSREPO_RESULT(ResultCode::OK);
275}
276
277void Cmd::print_capture(std::ostream &os, std::shared_ptr<manifest::Capture> capture)
278{
279 for (auto repo : capture->get_items())
280 {
281 os << std::endl;
282 os << std::setw(20) << std::left << repo->get_path() << repo->get_revision();
283 }
284}
285
286Result Cmd::print_esysrepo_note(std::ostream &os, const std::string &note_text)
287{
288 auto capture = std::make_shared<manifest::Capture>();
289 auto result = load_capture(capture, note_text);
290 if (result.error()) ESYSREPO_RESULT(result);
291
292 print_capture(os, capture);
293 return ESYSREPO_RESULT(ResultCode::OK);
294}
295
296void Cmd::set_sub_args(const std::vector<std::string> &sub_args)
297{
298 m_sub_args = sub_args;
299}
300
301const std::vector<std::string> &Cmd::get_sub_args() const
302{
303 return m_sub_args;
304}
305
306void Cmd::set_time(bool time)
307{
308 m_time = time;
309}
310
311bool Cmd::get_time() const
312{
313 return m_time;
314}
315
316void Cmd::set_delta_time(bool delta_time)
317{
318 m_delta_time = delta_time;
319}
320
321bool Cmd::get_delta_time() const
322{
323 return m_delta_time;
324}
325
326void Cmd::set_groups(const std::vector<std::string> &groups)
327{
328 m_groups = groups;
329}
330
331const std::vector<std::string> &Cmd::get_groups() const
332{
333 return m_groups;
334}
335
336void Cmd::set_cli_cmd(cli::Cmd *cli_cmd)
337{
338 m_cli_cmd = cli_cmd;
339}
340
341cli::Cmd *Cmd::get_cli_cmd()
342{
343 return m_cli_cmd;
344}
345
346void Cmd::set_app_base(cli::AppBase *app_base)
347{
348 m_app_base = app_base;
349}
350
351cli::AppBase *Cmd::get_app_base()
352{
353 return m_app_base;
354}
355
356int Cmd::process_sub_args_as_git_repo_path(const std::string &input_path)
357{
358 if (input_path.empty()) return 0;
359
360 boost::filesystem::path the_input_path = input_path;
361 if (the_input_path == ".") the_input_path = boost::filesystem::current_path();
362
363 boost::filesystem::path git_repo = Cmd::find_git_repo_path(the_input_path.string());
364 boost::filesystem::path workspace_path = get_workspace_path();
365
366 if (workspace_path.empty())
367 {
368 workspace_path = Cmd::find_workspace_path(the_input_path.string());
369
370 if (workspace_path.empty())
371 {
372 error("Requires ESysRepo to be installed first.");
373 return -1;
374 }
375 else
376 set_workspace_path(workspace_path.string());
377 }
378
379 if (git_repo.empty())
380 {
381 // Folder may not be cloned yet, or the argument is a manifest path.
382 auto repo = get_manifest()->find_repo_by_path(the_input_path.generic_string());
383 if (repo == nullptr)
384 {
385 error("The given path doesn't exists : " + the_input_path.string());
386 return -1;
387 }
388 m_input_rel_git_repo_paths.push_back(repo->get_path());
389 return 0;
390 }
391
392 if (!boost::filesystem::exists(the_input_path))
393 {
394 // The input path doesn't exist
395 // Couldn't it be the relative path of a git repo in the manifest?
396 auto repo = get_manifest()->find_repo_by_path(the_input_path.generic_string());
397 if (repo == nullptr)
398 {
399 error("The given path doesn't exists : " + the_input_path.string());
400 return -1;
401 }
402
403 // This was indeed the relative path of a git repo in the manifest
404 git_repo = repo->get_path();
405 }
406 else
407 {
408 // We need to find which repo this is related too
409 boost::filesystem::path git_rel_path = boost::filesystem::relative(git_repo, workspace_path);
410
411 if (git_rel_path.empty())
412 {
413 error("The following git repo doesn't seem to be known by ESysRepo: " + git_repo.string());
414 return -1;
415 }
416 git_repo = git_rel_path;
417 }
418 m_input_rel_git_repo_paths.push_back(git_repo.generic_path().string());
419 return 0;
420}
421
422Result Cmd::process_sub_args_as_git_repo_paths()
423{
424 if (get_manifest() == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_IS_NULLPTR);
425
426 int result = 0;
427
428 for (auto &input_path : get_sub_args())
429 {
430 int local_result = process_sub_args_as_git_repo_path(input_path);
431 if (local_result < 0) --result;
432 }
433 if (result < 0)
434 return ESYSREPO_RESULT(ResultCode::CMD_GENERIC_RAW_ERROR, result);
435 else
436 return ESYSREPO_RESULT(ResultCode::OK);
437}
438
439const std::vector<std::string> &Cmd::get_input_git_repo_paths() const
440{
441 return m_input_rel_git_repo_paths;
442}
443
444void Cmd::set_config_folder(std::shared_ptr<ConfigFolder> config_folder)
445{
446 m_config_folder = config_folder;
447}
448
449std::shared_ptr<ConfigFolder> Cmd::get_config_folder()
450{
451 return m_config_folder;
452}
453
454std::shared_ptr<ConfigFolder> Cmd::get_config_folder() const
455{
456 return m_config_folder;
457}
458
459void Cmd::set_loader(std::shared_ptr<manifest::Loader> loader)
460{
461 m_loader = loader;
462}
463
464std::shared_ptr<manifest::Loader> Cmd::get_loader()
465{
466 return m_loader;
467}
468
469Result Cmd::open_esysrepo_folder()
470{
471 auto config_folder = std::make_shared<ConfigFolder>();
472
473 set_config_folder(config_folder);
474
475 boost::filesystem::path rel_parent_path = boost::filesystem::relative(get_workspace_path());
476 Result result = config_folder->open(get_workspace_path());
477 if (result.error())
478 error("Failed to open esysrepo folder : " + rel_parent_path.string());
479 else
480 debug(0, "Opened esysrepo folder : " + rel_parent_path.string());
481
482 return ESYSREPO_RESULT(result);
483}
484
485Result Cmd::load_manifest()
486{
487 if (get_config_folder() == nullptr) return ESYSREPO_RESULT(ResultCode::CMD_CONFIG_FOLDER_NULLPTR);
488
489 if (get_loader() == nullptr) set_loader(std::make_shared<manifest::Loader>());
490 if (get_manifest() == nullptr) set_manifest(std::make_shared<Manifest>());
491 get_manifest()->clear();
492
493 get_loader()->set_manifest(get_manifest());
494 get_loader()->set_config_folder(get_config_folder());
495
496 get_loader()->set_logger_if(get_logger_if());
497 Result result = get_loader()->run();
498 return ESYSREPO_RESULT(result);
499}
500
501Result Cmd::ensure_manifest_loaded()
502{
503 if (get_manifest() != nullptr) return ESYSREPO_RESULT(ResultCode::OK);
504 if (get_config_folder() == nullptr)
505 {
506 Result result = open_esysrepo_folder();
507 if (result.error()) return ESYSREPO_RESULT(result);
508 }
509 return ESYSREPO_RESULT(load_manifest());
510}
511
512void Cmd::set_nested_context(NestedContext *nested_context)
513{
514 m_nested_context = nested_context;
515}
516
517NestedContext *Cmd::get_nested_context() const
518{
519 return m_nested_context;
520}
521
522Result Cmd::default_handling_folder_workspace()
523{
524 if (!get_folder().empty() && get_workspace_path().empty())
525 {
526 boost::filesystem::path path = exe::Cmd::find_workspace_path(get_folder());
527 if (path.empty())
528 {
529 std::string err_str = "Couldn't find a folder with ESysRepo from : " + get_folder();
530 error(err_str);
531 return ESYSREPO_RESULT(ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
532 }
533 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
534 set_workspace_path(path.string());
535 }
536 else if (!get_workspace_path().empty())
537 {
538 boost::filesystem::path path = exe::Cmd::find_workspace_path(get_workspace_path());
539 if (path.empty())
540 {
541 std::string err_str = "Couldn't find a folder with ESysRepo from : " + get_workspace_path();
542 error(err_str);
543 return ESYSREPO_RESULT(ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
544 }
545 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
546 set_workspace_path(path.string());
547 }
548 else
549 {
550 boost::filesystem::path path = exe::Cmd::find_workspace_path();
551 if (path.empty())
552 {
553 std::string err_str =
554 "Couldn't find a folder with ESysRepo from : " + boost::filesystem::current_path().string();
555 error(err_str);
556 return ESYSREPO_RESULT(ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
557 }
558 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
559 set_workspace_path(path.string());
560 }
561 return ESYSREPO_RESULT(ResultCode::OK);
562}
563
564Result Cmd::only_one_folder_or_workspace()
565{
566 if (!get_folder().empty() && !get_workspace_path().empty())
567 {
568 std::string err_str = "--folder and --workspace can't be specified at the same time";
569 error(err_str);
570 return ESYSREPO_RESULT(ResultCode::CMD_INCORRECT_PARAMETERS_COMBINATION, err_str);
571 }
572 else if (!get_folder().empty() || !get_workspace_path().empty())
573 {
574 boost::filesystem::path path;
575 if (!get_folder().empty())
576 path = get_folder();
577 else
578 path = get_workspace_path();
579 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
580 set_workspace_path(path.string());
581 }
582 else
583 set_workspace_path(boost::filesystem::current_path().lexically_normal().make_preferred().string());
584 return ESYSREPO_RESULT(ResultCode::OK);
585}
586
587Result Cmd::get_repo_branches(std::shared_ptr<manifest::Repository> repo, git::BranchType branch_type,
588 git::Branches &branches)
589{
590 auto git_helper = new_git_helper();
591
592 boost::filesystem::path rel_repo_path;
593 boost::filesystem::path repo_path = /*get_config_folder()->*/ get_workspace_path();
594 repo_path /= repo->get_path();
595 repo_path = boost::filesystem::absolute(repo_path).lexically_normal().make_preferred();
596 rel_repo_path = boost::filesystem::relative(repo_path);
597
598 // auto rel_repo_path = rel_repo_path.string();
599 // auto repo_path = repo_path.string();
600
601 branches.clear();
602
603 Result result = git_helper->open(repo_path.string(), log::Level::DEBUG);
604 if (result.error()) return ESYSREPO_RESULT(result);
605
606 auto last_commit = std::make_shared<git::CommitHash>();
607 result = get_git()->get_last_commit(*last_commit);
608 if (result.error()) last_commit.reset();
609
610 result = git_helper->get_branches(branches, branch_type, log::Level::DEBUG);
611 if (result.error())
612 {
613 git_helper->close_on_error(log::Level::DEBUG);
614 return ESYSREPO_RESULT(result);
615 }
616 result = git_helper->close(log::Level::DEBUG);
617 return ESYSREPO_RESULT(result);
618}
619
620std::string Cmd::get_extra_start_msg()
621{
622 return "";
623}
624
625void Cmd::set_console_os(std::ostream *console_os)
626{
627 m_console_os = console_os;
628}
629
630std::ostream *Cmd::get_console_os()
631{
632 return m_console_os;
633}
634
635std::shared_ptr<esys::log::Mngr> Cmd::get_logger_mngr()
636{
637 if (m_logger_mngr == nullptr) m_logger_mngr = esys::log::Mngr::get();
638 return m_logger_mngr;
639}
640
641const std::string &Cmd::get_logger_name() const
642{
643 return m_logger_name;
644}
645
646void Cmd::set_logger_name(const std::string &logger_name)
647{
648 m_logger_name = logger_name;
649}
650
651int Cmd::create_logger(const std::string &path)
652{
653 auto logger = get_logger_mngr()->new_logger(log::LoggerType::SPDLOG, get_logger_name());
654 if (logger == nullptr) return -1;
655
656 log::Level level = log::Level::DEBUG;
657
658 logger->add_console(level);
659 boost::filesystem::path log_path = path;
660 if (log_path.empty()) log_path = boost::filesystem::current_path();
661 log_path /= "log.txt";
662 logger->add_basic_file(log_path.string());
663 logger->set_log_level(level);
664 logger->set_debug_level(5);
665 logger->set_flush_log_level(level);
666 set_logger_if(logger);
667 return 0;
668}
669
670void Cmd::debug(int level, const std::string &msg)
671{
672 clean_cout();
673
674 User::debug(level, msg);
675}
676
677void Cmd::info(const std::string &msg)
678{
679 clean_cout();
680
681 User::info(msg);
Leak_StillReachable: 16 bytes in 1 blocks are still reachable in loss record 1 of 5 ↗
  1. src/esys/repo/exe/cmd.cpp:764 esys::repo::exe::Cmd::print_cmd_name() ↗
  2. src/esys/repo/exe/cmd.cpp:110 esys::repo::exe::Cmd::run() ↗
  3. src/esys/repo/exe/test/cmdinit01.cpp:57 esys::repo::exe::test::CmdInit01::test_method() ↗
  4. src/esys/repo/exe/test/cmdinit01.cpp:41 esys::repo::exe::test::CmdInit01_invoker() ↗
  5. include/boost/function/function_template.hpp:117 boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&)
  6. function/function_template.hpp:771 boost::function0<void>::operator()() const
682}
683
684void Cmd::warn(const std::string &msg)
685{
686 clean_cout();
687
688 User::warn(msg);
689}
690
691void Cmd::error(const std::string &msg)
692{
693 clean_cout();
694
695 User::error(msg);
696}
697
698void Cmd::critical(const std::string &msg)
699{
700 clean_cout();
701
702 User::critical(msg);
703}
704
705void Cmd::log(log::Level level, const std::string &msg)
706{
707 clean_cout();
708
709 User::log(level, msg);
710}
711
712void Cmd::log(const std::string &msg, log::Level level, int debug_level)
713{
714 clean_cout();
715
716 User::log(msg, level, debug_level);
717}
718
719void Cmd::clean_cout()
720{
721 /*log::ConsoleLockGuard<log::User> lock(this);
722
723 std::string s = " ";
724 std::cout << "\r";
725
726 for (int idx = 0; idx < 8; ++idx)
727 std::cout << s;
728 std::cout << "\r"; */
729}
730
731void Cmd::set_print_cmd_name_by_base(bool print_cmd_name_by_base)
732{
733 m_print_cmd_name_by_base = print_cmd_name_by_base;
734}
735
736bool Cmd::get_print_cmd_name_by_base() const
737{
738 return m_print_cmd_name_by_base;
739}
740
741void Cmd::set_print_result_summary(bool print_result_summary)
742{
743 m_print_result_summary = print_result_summary;
744}
745
746bool Cmd::get_print_result_summary() const
747{
748 return m_print_result_summary;
749}
750
751std::shared_ptr<GitHelper> Cmd::new_git_helper()
752{
753 auto git_helper = std::make_shared<GitHelper>(get_git(), get_logger_if());
754
755 get_git()->set_logger_if(git_helper);
756 return git_helper;
757}
758
759void Cmd::print_cmd_name()
760{
761 std::ostringstream oss;
762
763 print_cmd_name(oss);
764 info(oss.str());
765}
766
767void Cmd::print_cmd_name(std::ostream &os)
768{
769 os << get_name() + " ...";
770}
771
772Result Cmd::generic_error(int error)
773{
774 if (error < 0)
775 return ESYSREPO_RESULT(ResultCode::CMD_GENERIC_RAW_ERROR, error);
776 else
777 return ESYSREPO_RESULT(ResultCode::OK);
778}
779
780} // namespace esys::repo::exe