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