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()) return 0;
380
381 if (!boost::filesystem::exists(the_input_path))
382 {
383 // The input path doesn't exist
384 // Couldn't it be the relative path of a git repo in the manifest?
385 auto repo = get_manifest()->find_repo_by_path(the_input_path.string());
386 if (repo == nullptr)
387 {
388 error("The given path doesn't exists : " + the_input_path.string());
389 return -1;
390 }
391
392 // This was indeed the relative path of a git repo in the manifest
393 git_repo = the_input_path.string();
394 }
395 else
396 {
397 // We need to find which repo this is related too
398 boost::filesystem::path git_rel_path = boost::filesystem::relative(git_repo, workspace_path);
399
400 if (git_rel_path.empty())
401 {
402 error("The following git repo doesn't seem to be known by ESysRepo: " + git_repo.string());
403 return -1;
404 }
405 git_repo = git_rel_path;
406 }
407 m_input_rel_git_repo_paths.push_back(git_repo.generic_path().string());
408 return 0;
409}
410
411Result Cmd::process_sub_args_as_git_repo_paths()
412{
413 if (get_manifest() == nullptr) return ESYSREPO_RESULT(ResultCode::MANIFEST_IS_NULLPTR);
414
415 int result = 0;
416
417 for (auto &input_path : get_sub_args())
418 {
419 int local_result = process_sub_args_as_git_repo_path(input_path);
420 if (local_result < 0) --result;
421 }
422 if (result < 0)
423 return ESYSREPO_RESULT(ResultCode::CMD_GENERIC_RAW_ERROR, result);
424 else
425 return ESYSREPO_RESULT(ResultCode::OK);
426}
427
428const std::vector<std::string> &Cmd::get_input_git_repo_paths() const
429{
430 return m_input_rel_git_repo_paths;
431}
432
433void Cmd::set_config_folder(std::shared_ptr<ConfigFolder> config_folder)
434{
435 m_config_folder = config_folder;
436}
437
438std::shared_ptr<ConfigFolder> Cmd::get_config_folder()
439{
440 return m_config_folder;
441}
442
443std::shared_ptr<ConfigFolder> Cmd::get_config_folder() const
444{
445 return m_config_folder;
446}
447
448void Cmd::set_loader(std::shared_ptr<manifest::Loader> loader)
449{
450 m_loader = loader;
451}
452
453std::shared_ptr<manifest::Loader> Cmd::get_loader()
454{
455 return m_loader;
456}
457
458Result Cmd::open_esysrepo_folder()
459{
460 auto config_folder = std::make_shared<ConfigFolder>();
461
462 set_config_folder(config_folder);
463
464 boost::filesystem::path rel_parent_path = boost::filesystem::relative(get_workspace_path());
465 Result result = config_folder->open(get_workspace_path());
466 if (result.error())
467 error("Failed to open esysrepo folder : " + rel_parent_path.string());
468 else
469 debug(0, "Opened esysrepo folder : " + rel_parent_path.string());
470
471 return ESYSREPO_RESULT(result);
472}
473
474Result Cmd::load_manifest()
475{
476 if (get_config_folder() == nullptr) return ESYSREPO_RESULT(ResultCode::CMD_CONFIG_FOLDER_NULLPTR);
477
478 if (get_loader() == nullptr) set_loader(std::make_shared<manifest::Loader>());
479 if (get_manifest() == nullptr) set_manifest(std::make_shared<Manifest>());
480 get_manifest()->clear();
481
482 get_loader()->set_manifest(get_manifest());
483 get_loader()->set_config_folder(get_config_folder());
484
485 get_loader()->set_logger_if(get_logger_if());
486 Result result = get_loader()->run();
487 return ESYSREPO_RESULT(result);
488}
489
490Result Cmd::default_handling_folder_workspace()
491{
492 if (!get_folder().empty() && get_workspace_path().empty())
493 {
494 boost::filesystem::path path = exe::Cmd::find_workspace_path(get_folder());
495 if (path.empty())
496 {
497 std::string err_str = "Couldn't find a folder with ESysRepo from : " + get_folder();
498 error(err_str);
499 return ESYSREPO_RESULT(ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
500 }
501 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
502 set_workspace_path(path.string());
503 }
504 else if (!get_workspace_path().empty())
505 {
506 boost::filesystem::path path = exe::Cmd::find_workspace_path(get_workspace_path());
507 if (path.empty())
508 {
509 std::string err_str = "Couldn't find a folder with ESysRepo from : " + get_workspace_path();
510 error(err_str);
511 return ESYSREPO_RESULT(ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
512 }
513 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
514 set_workspace_path(path.string());
515 }
516 else
517 {
518 boost::filesystem::path path = exe::Cmd::find_workspace_path();
519 if (path.empty())
520 {
521 std::string err_str =
522 "Couldn't find a folder with ESysRepo from : " + boost::filesystem::current_path().string();
523 error(err_str);
524 return ESYSREPO_RESULT(ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
525 }
526 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
527 set_workspace_path(path.string());
528 }
529 return ESYSREPO_RESULT(ResultCode::OK);
530}
531
532Result Cmd::only_one_folder_or_workspace()
533{
534 if (!get_folder().empty() && !get_workspace_path().empty())
535 {
536 std::string err_str = "--folder and --workspace can't be specified at the same time";
537 error(err_str);
538 return ESYSREPO_RESULT(ResultCode::CMD_INCORRECT_PARAMETERS_COMBINATION, err_str);
539 }
540 else if (!get_folder().empty() || !get_workspace_path().empty())
541 {
542 boost::filesystem::path path;
543 if (!get_folder().empty())
544 path = get_folder();
545 else
546 path = get_workspace_path();
547 path = boost::filesystem::absolute(path).lexically_normal().make_preferred();
548 set_workspace_path(path.string());
549 }
550 else
551 set_workspace_path(boost::filesystem::current_path().lexically_normal().make_preferred().string());
552 return ESYSREPO_RESULT(ResultCode::OK);
553}
554
555Result Cmd::get_repo_branches(std::shared_ptr<manifest::Repository> repo, git::BranchType branch_type,
556 git::Branches &branches)
557{
558 auto git_helper = new_git_helper();
559
560 boost::filesystem::path rel_repo_path;
561 boost::filesystem::path repo_path = /*get_config_folder()->*/ get_workspace_path();
562 repo_path /= repo->get_path();
563 repo_path = boost::filesystem::absolute(repo_path).lexically_normal().make_preferred();
564 rel_repo_path = boost::filesystem::relative(repo_path);
565
566 // auto rel_repo_path = rel_repo_path.string();
567 // auto repo_path = repo_path.string();
568
569 branches.clear();
570
571 Result result = git_helper->open(repo_path.string(), log::Level::DEBUG);
572 if (result.error()) return ESYSREPO_RESULT(result);
573
574 auto last_commit = std::make_shared<git::CommitHash>();
575 result = get_git()->get_last_commit(*last_commit);
576 if (result.error()) last_commit.reset();
577
578 result = git_helper->get_branches(branches, branch_type, log::Level::DEBUG);
579 if (result.error())
580 {
581 git_helper->close_on_error(log::Level::DEBUG);
582 return ESYSREPO_RESULT(result);
583 }
584 result = git_helper->close(log::Level::DEBUG);
585 return ESYSREPO_RESULT(result);
586}
587
588std::string Cmd::get_extra_start_msg()
589{
590 return "";
591}
592
593void Cmd::set_console_os(std::ostream *console_os)
594{
595 m_console_os = console_os;
596}
597
598std::ostream *Cmd::get_console_os()
599{
600 return m_console_os;
601}
602
603std::shared_ptr<esys::log::Mngr> Cmd::get_logger_mngr()
604{
605 if (m_logger_mngr == nullptr) m_logger_mngr = esys::log::Mngr::get();
606 return m_logger_mngr;
607}
608
609const std::string &Cmd::get_logger_name() const
610{
611 return m_logger_name;
612}
613
614void Cmd::set_logger_name(const std::string &logger_name)
615{
616 m_logger_name = logger_name;
617}
618
619int Cmd::create_logger(const std::string &path)
620{
621 auto logger = get_logger_mngr()->new_logger(log::LoggerType::SPDLOG, get_logger_name());
622 if (logger == nullptr) return -1;
623
624 log::Level level = log::Level::DEBUG;
625
626 logger->add_console(level);
627 boost::filesystem::path log_path = path;
628 if (log_path.empty()) log_path = boost::filesystem::current_path();
629 log_path /= "log.txt";
630 logger->add_basic_file(log_path.string());
631 logger->set_log_level(level);
632 logger->set_debug_level(5);
633 logger->set_flush_log_level(level);
634 set_logger_if(logger);
635 return 0;
636}
637
638void Cmd::debug(int level, const std::string &msg)
639{
640 clean_cout();
641
642 User::debug(level, msg);
643}
644
645void Cmd::info(const std::string &msg)
646{
647 clean_cout();
648
649 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:732 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
650}
651
652void Cmd::warn(const std::string &msg)
653{
654 clean_cout();
655
656 User::warn(msg);
657}
658
659void Cmd::error(const std::string &msg)
660{
661 clean_cout();
662
663 User::error(msg);
664}
665
666void Cmd::critical(const std::string &msg)
667{
668 clean_cout();
669
670 User::critical(msg);
671}
672
673void Cmd::log(log::Level level, const std::string &msg)
674{
675 clean_cout();
676
677 User::log(level, msg);
678}
679
680void Cmd::log(const std::string &msg, log::Level level, int debug_level)
681{
682 clean_cout();
683
684 User::log(msg, level, debug_level);
685}
686
687void Cmd::clean_cout()
688{
689 /*log::ConsoleLockGuard<log::User> lock(this);
690
691 std::string s = " ";
692 std::cout << "\r";
693
694 for (int idx = 0; idx < 8; ++idx)
695 std::cout << s;
696 std::cout << "\r"; */
697}
698
699void Cmd::set_print_cmd_name_by_base(bool print_cmd_name_by_base)
700{
701 m_print_cmd_name_by_base = print_cmd_name_by_base;
702}
703
704bool Cmd::get_print_cmd_name_by_base() const
705{
706 return m_print_cmd_name_by_base;
707}
708
709void Cmd::set_print_result_summary(bool print_result_summary)
710{
711 m_print_result_summary = print_result_summary;
712}
713
714bool Cmd::get_print_result_summary() const
715{
716 return m_print_result_summary;
717}
718
719std::shared_ptr<GitHelper> Cmd::new_git_helper()
720{
721 auto git_helper = std::make_shared<GitHelper>(get_git(), get_logger_if());
722
723 get_git()->set_logger_if(git_helper);
724 return git_helper;
725}
726
727void Cmd::print_cmd_name()
728{
729 std::ostringstream oss;
730
731 print_cmd_name(oss);
732 info(oss.str());
733}
734
735void Cmd::print_cmd_name(std::ostream &os)
736{
737 os << get_name() + " ...";
738}
739
740Result Cmd::generic_error(int error)
741{
742 if (error < 0)
743 return ESYSREPO_RESULT(ResultCode::CMD_GENERIC_RAW_ERROR, error);
744 else
745 return ESYSREPO_RESULT(ResultCode::OK);
746}
747
748} // namespace esys::repo::exe