From 9107ea45cb11fff79239ae83562b0daf835efe27 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 17 May 2020 21:23:43 -0400 Subject: [PATCH] added git options in ini moved all the code from git_command to the git tool changed task_conf_holder to make the git tool since it's getting pretty complex fixed task name in options not supporting super fixed pipes behaving weirdly when copying process objects --- mob.ini | 16 +- src/commands.cpp | 220 +------------------------- src/commands.h | 29 ---- src/conf.cpp | 29 +++- src/process.cpp | 5 + src/process.h | 2 +- src/tasks/boost_di.cpp | 2 +- src/tasks/gtest.cpp | 2 +- src/tasks/libffi.cpp | 2 +- src/tasks/lz4.cpp | 2 +- src/tasks/modorganizer.cpp | 2 +- src/tasks/ncc.cpp | 2 +- src/tasks/nmm.cpp | 2 +- src/tasks/python.cpp | 2 +- src/tasks/spdlog.cpp | 2 +- src/tasks/task.cpp | 79 +++++++++- src/tasks/task.h | 34 +---- src/tasks/usvfs.cpp | 2 +- src/tools/cmake.cpp | 2 +- src/tools/downloader.cpp | 32 ++-- src/tools/extractor.cpp | 40 ++--- src/tools/git.cpp | 289 ++++++++++++++++++++++++++++++++++- src/tools/patcher.cpp | 26 ++-- src/tools/process_runner.cpp | 4 +- src/tools/tools.cpp | 13 +- src/tools/tools.h | 59 ++++++- 26 files changed, 553 insertions(+), 346 deletions(-) diff --git a/mob.ini b/mob.ini index 0afa9f2..047d4cb 100644 --- a/mob.ini +++ b/mob.ini @@ -8,9 +8,19 @@ file_log_level = 5 log_file = mob.log [options] -mo_org = ModOrganizer2 -mo_branch = master -no_pull = false +mo_org = ModOrganizer2 +mo_branch = master +no_pull = false +ignore_ts = false + +git_username = +git_email = + +set_origin_remote = false +remote_username = +remote_key = +remote_no_push_upstream = false +remote_push_default_origin = false [tools] sevenz = 7z.exe diff --git a/src/commands.cpp b/src/commands.cpp index d8ac09e..2bd714a 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -758,9 +758,9 @@ clipp::group git_command::do_group() & clipp::value("NAME") >> remote_) % "name of new remote", - (clipp::required("-u", "--url") - & clipp::value("URL") >> url_) - % "remote URL", + (clipp::required("-u", "--username") + & clipp::value("USERNAME") >> username_) + % "git username", (clipp::option("-k", "--key") & clipp::value("PATH") >> key_) @@ -840,27 +840,8 @@ void git_command::do_set_remotes() for (auto&& r : repos) { u8cout << "setting up " << path_to_utf8(r.filename()) << "\n"; - - set_config(r, "user.name", username_); - set_config(r, "user.email", email_); - - if (!has_remote(r, "upstream")) - { - const auto gf = git_file(r); - - rename_remote(r, "origin", "upstream"); - - if (nopush_) - set_remote_push_url(r, "upstream", "nopushurl"); - - add_remote(r, "origin", make_url(gf)); - - if (push_default_) - set_config(r, "remote.pushdefault", "origin"); - - if (!key_.empty()) - set_config(r, "remote.origin.puttykeyfile", key_); - } + git::set_credentials(r, username_, email_); + git::set_remote(r, username_, key_, nopush_, push_default_); } } @@ -870,22 +851,12 @@ void git_command::do_add_remote() u8cout << "adding remote '" << remote_ << "' " - << "from '" << url_ << "' to repos\n"; + << "from '" << username_ << "' to repos\n"; for (auto&& r : repos) { u8cout << path_to_utf8(r.filename()) << "\n"; - - if (!has_remote(r, remote_)) - { - add_remote(r, remote_, url_); - - if (push_default_) - set_config(r, "remote.pushdefault", remote_); - - if (!key_.empty()) - set_config(r, "remote." + remote_ + ".puttykeyfile", key_); - } + git::add_remote(r, remote_, username_, key_, push_default_); } } @@ -901,31 +872,7 @@ void git_command::do_ignore_ts() for (auto&& r : repos) { u8cout << path_to_utf8(r.filename()) << "\n"; - - for (auto&& e : fs::recursive_directory_iterator(r)) - { - if (!e.is_regular_file()) - continue; - - const auto p = e.path(); - - if (!path_to_utf8(p.extension()).ends_with(".ts")) - continue; - - const auto rp = fs::relative(p, r); - - if (is_tracked(r, rp)) - { - u8cout << " . " << path_to_utf8(rp) << "\n"; - set_assume_unchanged(r, rp, tson_); - } - else - { - u8cout - << " . " - << path_to_utf8(rp) << " (skipping, not tracked)\n"; - } - } + git::ignore_ts(r, tson_); } } @@ -960,157 +907,6 @@ std::vector git_command::get_repos() const return v; } -void git_command::set_config( - const fs::path& repo, const std::string& key, const std::string& value) -{ - auto p = process() - .binary(git::binary()) - .arg("config") - .arg(key) - .arg(value) - .cwd(repo); - - p.run(); - p.join(); -} - -bool git_command::has_remote(const fs::path& repo, const std::string& name) -{ - auto p = process() - .binary(git::binary()) - .flags(process::allow_failure) - .stderr_level(context::level::debug) - .arg("remote") - .arg("show") - .arg(name) - .cwd(repo); - - p.run(); - p.join(); - - return (p.exit_code() == 0); -} - -void git_command::rename_remote( - const fs::path& repo, - const std::string& from, const std::string& to) -{ - auto p = process() - .binary(git::binary()) - .arg("remote") - .arg("rename") - .arg(from) - .arg(to) - .cwd(repo); - - p.run(); - p.join(); -} - -void git_command::add_remote( - const fs::path& repo, - const std::string& name, const std::string& url) -{ - auto p = process() - .binary(git::binary()) - .arg("remote") - .arg("add") - .arg(name) - .arg(url) - .cwd(repo); - - p.run(); - p.join(); -} - -void git_command::set_remote_push_url( - const fs::path& repo, - const std::string& remote, const std::string& url) -{ - auto p = process() - .binary(git::binary()) - .arg("remote") - .arg("set-url") - .arg("--push") - .arg(remote) - .arg(url) - .cwd(repo); - - p.run(); - p.join(); -} - -void git_command::set_assume_unchanged( - const fs::path& repo, const fs::path& relative_file, bool on) -{ - auto p = process() - .binary(git::binary()) - .arg("update-index") - .arg(on ? "--assume-unchanged" : "--no-assume-unchanged") - .arg(relative_file, process::forward_slashes) - .cwd(repo); - - p.run(); - p.join(); -} - -bool git_command::is_tracked( - const fs::path& repo, const fs::path& relative_file) -{ - auto p = process() - .binary(git::binary()) - .stdout_level(context::level::debug) - .stderr_level(context::level::debug) - .flags(process::allow_failure) - .arg("ls-files") - .arg("--error-unmatch") - .arg(relative_file, process::forward_slashes) - .cwd(repo); - - p.run(); - p.join(); - - return (p.exit_code() == 0); -} - -std::string git_command::git_file(const fs::path& repo) -{ - auto p = process() - .binary(git::binary()) - .stdout_flags(process::keep_in_string) - .arg("remote") - .arg("get-url") - .arg("origin") - .cwd(repo); - - p.run(); - p.join(); - - const std::string out = p.stdout_string(); - - const auto last_slash = out.find_last_of("/"); - if (last_slash == std::string::npos) - { - u8cerr << "bad get-url output '" << out << "'\n"; - throw bailed(); - } - - auto s = trim_copy(out.substr(last_slash + 1)); - - if (s.empty()) - { - u8cerr << "bad get-url output '" << out << "'\n"; - throw bailed(); - } - - return s; -} - -std::string git_command::make_url(const std::string& git_file) -{ - return "git@github.com:" + username_ + "/" + git_file; -} - cmake_command::cmake_command() : command(requires_options) diff --git a/src/commands.h b/src/commands.h index 97bf2a9..f5ac4b0 100644 --- a/src/commands.h +++ b/src/commands.h @@ -185,7 +185,6 @@ private: std::string email_; std::string key_; std::string remote_; - std::string url_; bool tson_ = false; bool nopush_ = false; bool push_default_ = false; @@ -195,34 +194,6 @@ private: void do_ignore_ts(); std::vector get_repos() const; - - void set_config( - const fs::path& repo, - const std::string& key, const std::string& value); - - bool has_remote( - const fs::path& repo, - const std::string& name); - - void rename_remote( - const fs::path& repo, - const std::string& from, const std::string& to); - - void add_remote( - const fs::path& repo, - const std::string& name, const std::string& url); - - void set_remote_push_url( - const fs::path& repo, - const std::string& remote, const std::string& url); - - void set_assume_unchanged( - const fs::path& repo, const fs::path& relative_file, bool on); - - bool is_tracked(const fs::path& repo, const fs::path& relative_file); - - std::string git_file(const fs::path& repo); - std::string make_url(const std::string& git_file); }; diff --git a/src/conf.cpp b/src/conf.cpp index bee73d8..8aabafe 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -3,6 +3,7 @@ #include "utility.h" #include "context.h" #include "process.h" +#include "tasks/task.h" #include "tools/tools.h" namespace mob @@ -84,6 +85,18 @@ std::string conf::get_for_task( break; } + if (task == map_.end()) + { + for (auto&& tn : task_names) + { + if (is_super_task(tn)) + { + task = map_.find("super"); + break; + } + } + } + if (task == map_.end()) return get_global(section, key); @@ -102,7 +115,7 @@ void conf::set_for_task( const std::string& task_name, const std::string& section, const std::string& key, const std::string& value) { - // make sure it exists, will throw if it doesn't + // make sure the key exists, will throw if it doesn't get_global(section, key); map_[task_name][section][key] = value; @@ -651,6 +664,9 @@ void parse_section( } else { + if (!task_exists(task)) + ini_error(ini, i, "task '" + task + "' doesn't exist"); + conf::set_for_task(task, section, k, v); } @@ -846,9 +862,20 @@ void init_options( const auto po = parse_option(o); if (po.task.empty()) + { conf::set_global(po.section, po.key, po.value); + } else + { + if (!task_exists(po.task)) + { + gcx().bail_out(context::generic, + "task '{}' doesn't exist (command line option)", + po.task); + } + conf::set_for_task(po.task, po.section, po.key, po.value); + } } } diff --git a/src/process.cpp b/src/process.cpp index e4324e9..e79ad9f 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -226,7 +226,12 @@ process::impl::impl(const impl& i) process::impl& process::impl::operator=(const impl& i) { + handle = {}; + job = {}; interrupt = i.interrupt.load(); + stdout_pipe = {}; + stderr_pipe = {}; + return *this; } diff --git a/src/process.h b/src/process.h index 2081ea3..03b5b39 100644 --- a/src/process.h +++ b/src/process.h @@ -112,7 +112,7 @@ public: static process raw(const context& cx, const std::string& cmd); - static process pipe(const process& p) + static process pipe(process p) { return p; } diff --git a/src/tasks/boost_di.cpp b/src/tasks/boost_di.cpp index c31e93d..6a009ba 100644 --- a/src/tasks/boost_di.cpp +++ b/src/tasks/boost_di.cpp @@ -26,7 +26,7 @@ fs::path boost_di::source_path() void boost_di::do_fetch() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("boost-experimental", "di")) .branch("cpp14") .output(source_path())); diff --git a/src/tasks/gtest.cpp b/src/tasks/gtest.cpp index d2a1c1a..f75498a 100644 --- a/src/tasks/gtest.cpp +++ b/src/tasks/gtest.cpp @@ -31,7 +31,7 @@ void gtest::do_clean_for_rebuild() void gtest::do_fetch() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("google", "googletest")) .branch(version()) .output(source_path())); diff --git a/src/tasks/libffi.cpp b/src/tasks/libffi.cpp index a9b4e65..77d6196 100644 --- a/src/tasks/libffi.cpp +++ b/src/tasks/libffi.cpp @@ -26,7 +26,7 @@ fs::path libffi::source_path() void libffi::do_fetch() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("python","cpython-bin-deps")) .branch("libffi") .output(source_path())); diff --git a/src/tasks/lz4.cpp b/src/tasks/lz4.cpp index 8f9c4f6..fc8c098 100644 --- a/src/tasks/lz4.cpp +++ b/src/tasks/lz4.cpp @@ -72,7 +72,7 @@ void lz4::build_and_install_prebuilt() void lz4::fetch_from_source() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("lz4","lz4")) .branch(version()) .output(source_path())); diff --git a/src/tasks/modorganizer.cpp b/src/tasks/modorganizer.cpp index 6746d13..09f017c 100644 --- a/src/tasks/modorganizer.cpp +++ b/src/tasks/modorganizer.cpp @@ -58,7 +58,7 @@ void modorganizer::do_fetch() { initialize_super(super_path()); - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url(task_conf().mo_org(), repo_)) .branch(task_conf().mo_branch()) .output(this_source_path())); diff --git a/src/tasks/ncc.cpp b/src/tasks/ncc.cpp index f67fb70..b5d5b6d 100644 --- a/src/tasks/ncc.cpp +++ b/src/tasks/ncc.cpp @@ -32,7 +32,7 @@ void ncc::do_clean_for_rebuild() void ncc::do_fetch() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url(task_conf().mo_org(), "modorganizer-NCC")) .branch(task_conf().mo_branch()) .output(source_path())); diff --git a/src/tasks/nmm.cpp b/src/tasks/nmm.cpp index 45e8b24..aa777e1 100644 --- a/src/tasks/nmm.cpp +++ b/src/tasks/nmm.cpp @@ -31,7 +31,7 @@ void nmm::do_clean_for_rebuild() void nmm::do_fetch() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("Nexus-Mods", "Nexus-Mod-Manager")) .branch(version()) .output(source_path())); diff --git a/src/tasks/python.cpp b/src/tasks/python.cpp index e85e230..a3a77fd 100644 --- a/src/tasks/python.cpp +++ b/src/tasks/python.cpp @@ -111,7 +111,7 @@ void python::build_and_install_prebuilt() void python::fetch_from_source() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("python", "cpython")) .branch(version()) .output(source_path())); diff --git a/src/tasks/spdlog.cpp b/src/tasks/spdlog.cpp index a2ac18e..fc2e33e 100644 --- a/src/tasks/spdlog.cpp +++ b/src/tasks/spdlog.cpp @@ -26,7 +26,7 @@ fs::path spdlog::source_path() void spdlog::do_fetch() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url("gabime", "spdlog")) .branch(version()) .output(source_path())); diff --git a/src/tasks/task.cpp b/src/tasks/task.cpp index 6ddc6b2..71f9ff9 100644 --- a/src/tasks/task.cpp +++ b/src/tasks/task.cpp @@ -151,6 +151,37 @@ void run_all_tasks() run_tasks(tasks); } +bool task_exists(const std::string& name) +{ + if (name == "super") + return true; + + for (auto&& t : g_all_tasks) + { + for (auto&& n : t->names()) + { + if (n == name) + return true; + } + } + + return false; +} + +bool is_super_task(const std::string& name) +{ + for (auto& t : g_all_tasks) + { + for (auto&& tn : t->names()) + { + if (tn == name) + return t->is_super(); + } + } + + return false; +} + struct task::thread_context { @@ -164,6 +195,52 @@ struct task::thread_context }; +task_conf_holder::task_conf_holder(const task& t) + : task_(t) +{ +} + +std::string task_conf_holder::mo_org() +{ + return conf::option_by_name(task_.names(), "mo_org"); +} + +std::string task_conf_holder::mo_branch() +{ + return conf::option_by_name(task_.names(), "mo_branch"); +} + +bool task_conf_holder::no_pull() +{ + return conf::bool_option_by_name(task_.names(), "no_pull"); +} + +git task_conf_holder::make_git() +{ + git g(no_pull() ? git::clone : git::clone_or_pull); + + g.ignore_ts(conf::bool_option_by_name(task_.names(), "ignore_ts")); + + g.credentials( + conf::option_by_name(task_.names(), "git_username"), + conf::option_by_name(task_.names(), "git_email") + ); + + if (conf::bool_option_by_name(task_.names(), "set_origin_remote")) + { + g.remote( + conf::option_by_name(task_.names(), "remote_username"), + conf::option_by_name(task_.names(), "remote_key"), + conf::bool_option_by_name(task_.names(), "remote_no_push_upstream"), + conf::bool_option_by_name(task_.names(), "remote_push_default_origin") + ); + } + + return g; +} + + + task::task(std::vector names) : names_(std::move(names)), interrupted_(false) { @@ -290,7 +367,7 @@ void task::parallel(std::vector>> task_conf_holder task::task_conf() const { - return task_conf_holder(names_); + return task_conf_holder(*this); } void task::run() diff --git a/src/tasks/task.h b/src/tasks/task.h index f9b6ee4..428a313 100644 --- a/src/tasks/task.h +++ b/src/tasks/task.h @@ -25,41 +25,23 @@ void run_task(const std::string& name); void run_tasks(const std::vector& names);; void run_all_tasks(); void list_tasks(bool err=false); +bool task_exists(const std::string& name); +bool is_super_task(const std::string& name); class task_conf_holder { public: - task_conf_holder(std::vector names) - : names_(std::move(names)) - { - } + task_conf_holder(const task& t); - std::string mo_org() - { - return conf::option_by_name(names_, "mo_org"); - } + std::string mo_org(); + std::string mo_branch(); + bool no_pull(); - std::string mo_branch() - { - return conf::option_by_name(names_, "mo_branch"); - } - - bool no_pull() - { - return conf::bool_option_by_name(names_, "no_pull"); - } - - git::ops git_op() - { - if (no_pull()) - return git::clone; - else - return git::clone_or_pull2; - } + git make_git(); private: - std::vector names_; + const task& task_; }; diff --git a/src/tasks/usvfs.cpp b/src/tasks/usvfs.cpp index c588e5d..fc1c629 100644 --- a/src/tasks/usvfs.cpp +++ b/src/tasks/usvfs.cpp @@ -65,7 +65,7 @@ void usvfs::build_and_install_prebuilt() void usvfs::fetch_from_source() { - run_tool(git(task_conf().git_op()) + run_tool(task_conf().make_git() .url(make_github_url(task_conf().mo_org(), "usvfs")) .branch(version()) .output(source_path())); diff --git a/src/tools/cmake.cpp b/src/tools/cmake.cpp index 03fb1fd..f15f1dd 100644 --- a/src/tools/cmake.cpp +++ b/src/tools/cmake.cpp @@ -98,7 +98,7 @@ fs::path cmake::result() const void cmake::do_run() { if (root_.empty()) - cx_->bail_out(context::generic, "cmake output path is empty"); + cx().bail_out(context::generic, "cmake output path is empty"); const auto& g = get_generator(gen_); diff --git a/src/tools/downloader.cpp b/src/tools/downloader.cpp index e79092c..50d14c1 100644 --- a/src/tools/downloader.cpp +++ b/src/tools/downloader.cpp @@ -34,9 +34,9 @@ fs::path downloader::result() const void downloader::do_run() { - dl_.reset(new curl_downloader(cx_)); + dl_.reset(new curl_downloader(&cx())); - cx_->trace(context::net, "looking for already downloaded files"); + cx().trace(context::net, "looking for already downloaded files"); if (!file_.empty()) { @@ -58,9 +58,9 @@ void downloader::do_run() } - cx_->trace(context::net, "no cached downloads were found, will try:"); + cx().trace(context::net, "no cached downloads were found, will try:"); for (auto&& u : urls_) - cx_->trace(context::net, " . {}", u); + cx().trace(context::net, " . {}", u); // try them in order @@ -69,29 +69,29 @@ void downloader::do_run() if (file_.empty()) file_ = path_for_url(u); - cx_->trace(context::net, "trying {} into {}", u, file_); + cx().trace(context::net, "trying {} into {}", u, file_); dl_->start(u, file_); - cx_->trace(context::net, "waiting for download"); + cx().trace(context::net, "waiting for download"); dl_->join(); if (dl_->ok()) { - cx_->trace(context::net, "file {} downloaded", file_); + cx().trace(context::net, "file {} downloaded", file_); return; } - cx_->debug(context::net, "download failed"); + cx().debug(context::net, "download failed"); } if (interrupted()) { - cx_->trace(context::interruption, ""); + cx().trace(context::interruption, ""); return; } // all failed - cx_->bail_out(context::net, "all urls failed to download"); + cx().bail_out(context::net, "all urls failed to download"); } void downloader::do_interrupt() @@ -106,18 +106,18 @@ bool downloader::try_picking(const fs::path& file) { if (conf::redownload()) { - cx_->trace(context::redownload, "deleting {}", file); - op::delete_file(*cx_, file, op::optional); + cx().trace(context::redownload, "deleting {}", file); + op::delete_file(cx(), file, op::optional); } else { - cx_->trace(context::bypass, "picking {}", file_); + cx().trace(context::bypass, "picking {}", file_); return true; } } else { - cx_->trace(context::net, "no {}", file); + cx().trace(context::net, "no {}", file); } return false; @@ -134,13 +134,13 @@ fs::path downloader::path_for_url(const mob::url& u) const // sf downloads end with /download, strip it to get the filename const std::string strip = "/download"; - cx_->trace(context::net, + cx().trace(context::net, "url {} is sourceforge, stripping {} for filename", u, strip); if (url_string.ends_with(strip)) url_string = url_string.substr(0, url_string.size() - strip.size()); else - cx_->trace(context::net, "no need to strip {}", u); + cx().trace(context::net, "no need to strip {}", u); filename = mob::url(url_string).filename(); } diff --git a/src/tools/extractor.cpp b/src/tools/extractor.cpp index f50214a..6510e48 100644 --- a/src/tools/extractor.cpp +++ b/src/tools/extractor.cpp @@ -28,33 +28,33 @@ extractor& extractor::output(const fs::path& dir) void extractor::do_run() { - interruption_file ifile(*cx_, where_, "extractor"); + interruption_file ifile(cx(), where_, "extractor"); if (ifile.exists()) { - cx_->debug(context::generic, + cx().debug(context::generic, "previous extraction was interrupted; resuming"); } else if (fs::exists(where_)) { if (conf::reextract()) { - cx_->debug(context::reextract, "deleting {}", where_); - op::delete_directory(*cx_, where_, op::optional); + cx().debug(context::reextract, "deleting {}", where_); + op::delete_directory(cx(), where_, op::optional); } else { - cx_->debug(context::bypass, "directory {} already exists", where_); + cx().debug(context::bypass, "directory {} already exists", where_); return; } } - cx_->debug(context::generic, "extracting {} into {}", file_, where_); + cx().debug(context::generic, "extracting {} into {}", file_, where_); ifile.create(); - op::create_directories(*cx_, where_); - directory_deleter delete_output(*cx_, where_); + op::create_directories(cx(), where_); + directory_deleter delete_output(cx(), where_); // the -spe from 7z is supposed to figure out if there's a folder in the // archive with the same name as the target and extract its content to @@ -74,7 +74,7 @@ void extractor::do_run() if (file_.u8string().ends_with(u8".tar.gz")) { - cx_->trace(context::generic, "this is a tar.gz, piping"); + cx().trace(context::generic, "this is a tar.gz, piping"); auto extract_tar = process() .binary(binary()) @@ -119,13 +119,13 @@ void extractor::check_duplicate_directory(const fs::path& ifile) // check for a folder with the same name if (!fs::exists(where_ / dir_name)) { - cx_->trace(context::generic, + cx().trace(context::generic, "no duplicate subdir {}, leaving as-is", dir_name); return; } - cx_->trace(context::generic, + cx().trace(context::generic, "found subdir {} with same name as output dir; " "moving everything up one", dir_name); @@ -149,15 +149,15 @@ void extractor::check_duplicate_directory(const fs::path& ifile) { // don't know what to do with archives that have the // same directory _and_ other directories - cx_->bail_out(context::generic, + cx().bail_out(context::generic, "check_duplicate_directory: {} is yet another directory", e.path()); } - cx_->trace(context::generic, + cx().trace(context::generic, "assuming file {} is useless, deleting", e.path()); - op::delete_file(*cx_, e.path()); + op::delete_file(cx(), e.path()); } // now there should only be two things in this directory: another @@ -167,25 +167,25 @@ void extractor::check_duplicate_directory(const fs::path& ifile) // same name in it const auto temp_dir = where_ / (u8"_mob_" + dir_name.u8string()); - cx_->trace(context::generic, + cx().trace(context::generic, "renaming dir to {} to avoid clashes", temp_dir); if (fs::exists(temp_dir)) { - cx_->trace(context::generic, + cx().trace(context::generic, "temp dir {} already exists, deleting", temp_dir); - op::delete_directory(*cx_, temp_dir); + op::delete_directory(cx(), temp_dir); } - op::rename(*cx_, where_ / dir_name, temp_dir); + op::rename(cx(), where_ / dir_name, temp_dir); // move the content of the directory up for (auto e : fs::directory_iterator(temp_dir)) - op::move_to_directory(*cx_, e.path(), where_); + op::move_to_directory(cx(), e.path(), where_); // delete the old directory, which should be empty now - op::delete_directory(*cx_, temp_dir); + op::delete_directory(cx(), temp_dir); } } // namespace diff --git a/src/tools/git.cpp b/src/tools/git.cpp index 5720f2c..9b047a8 100644 --- a/src/tools/git.cpp +++ b/src/tools/git.cpp @@ -6,10 +6,60 @@ namespace mob { git::git(ops o) - : basic_process_runner("git"), op_(o) + : basic_process_runner("git"), op_(o), ignore_ts_(false) { } +void git::set_credentials( + const fs::path& repo, + const std::string& username, const std::string& email) +{ + git(ops(0)) + .output(repo) + .credentials(username, email) + .do_set_credentials(); +} + +void git::set_remote( + const fs::path& repo, + std::string username, std::string key, + bool no_push_upstream, bool push_default_origin) +{ + git(ops(0)) + .output(repo) + .remote(username, key, no_push_upstream, push_default_origin) + .do_set_remote(); +} + +void git::ignore_ts(const fs::path& repo, bool b) +{ + git(ops(0)) + .output(repo) + .ignore_ts(b) + .do_ignore_ts(); +} + +void git::add_remote( + const fs::path& repo, const std::string& remote_name, + const std::string& username, const std::string& key, bool push_default) +{ + git g(ops(0)); + g.output(repo); + + const auto gf = g.git_file(); + + if (!g.has_remote(remote_name)) + { + g.add_remote(remote_name, make_url(username, gf)); + + if (push_default) + g.set_config("remote.pushdefault", remote_name); + + if (!key.empty()) + g.set_config("remote." + remote_name + ".puttykeyfile", key); + } +} + fs::path git::binary() { return conf::tool_by_name("git"); @@ -33,6 +83,31 @@ git& git::output(const fs::path& dir) return *this; } +git& git::credentials(const std::string& username, const std::string& email) +{ + creds_username_ = username; + creds_email_ = email; + return *this; +} + +git& git::remote( + std::string username, std::string key, + bool no_push_upstream, bool push_default_origin) +{ + remote_username_ = username; + remote_key_ = key; + no_push_upstream_ = no_push_upstream; + push_default_origin_ = push_default_origin; + + return *this; +} + +git& git::ignore_ts(bool b) +{ + ignore_ts_ = b; + return *this; +} + void git::do_run() { if (url_.empty() || where_.empty()) @@ -40,8 +115,8 @@ void git::do_run() if (conf::redownload() || conf::reextract()) { - cx_->trace(context::rebuild, "deleting directory controlled by git"); - op::delete_directory(*cx_, where_, op::optional); + cx().trace(context::rebuild, "deleting directory controlled by git"); + op::delete_directory(cx(), where_, op::optional); } @@ -59,7 +134,7 @@ void git::do_run() break; } - case clone_or_pull2: + case clone_or_pull: { do_clone_or_pull(); break; @@ -67,7 +142,7 @@ void git::do_run() default: { - cx_->bail_out(context::generic, "git unknown op {}", op_); + cx().bail_out(context::generic, "git unknown op {}", op_); } } } @@ -83,7 +158,7 @@ bool git::do_clone() const fs::path dot_git = where_ / ".git"; if (fs::exists(dot_git)) { - cx_->trace(context::generic, "not cloning, {} exists", dot_git); + cx().trace(context::generic, "not cloning, {} exists", dot_git); return false; } @@ -101,6 +176,16 @@ bool git::do_clone() execute_and_join(); + + if (!creds_username_.empty() || !creds_email_.empty()) + do_set_credentials(); + + if (!remote_username_.empty()) + do_set_remote(); + + if (ignore_ts_) + do_ignore_ts(); + return true; } @@ -119,4 +204,196 @@ void git::do_pull() execute_and_join(); } +void git::do_set_credentials() +{ + cx().debug(context::generic, "setting up credentials"); + + if (!creds_username_.empty()) + set_config("user.name", creds_username_); + + if (!creds_email_.empty()) + set_config("user.email", creds_email_); +} + +void git::do_set_remote() +{ + if (has_remote("upstream")) + { + cx().trace(context::generic, "upstream remote already exists"); + return; + } + + const auto gf = git_file(); + + rename_remote("origin", "upstream"); + + if (no_push_upstream_) + set_remote_push("upstream", "nopushurl"); + + add_remote("origin", make_url(remote_username_, gf)); + + if (push_default_origin_) + set_config("remote.pushdefault", "origin"); + + if (!remote_key_.empty()) + set_config("remote.origin.puttykeyfile", remote_key_); +} + +void git::do_ignore_ts() +{ + for (auto&& e : fs::recursive_directory_iterator(where_)) + { + if (!e.is_regular_file()) + continue; + + const auto p = e.path(); + + if (!path_to_utf8(p.extension()).ends_with(".ts")) + continue; + + const auto rp = fs::relative(p, where_); + + if (is_tracked(rp)) + { + cx().trace(context::generic, " . {}", rp); + set_assume_unchanged(rp, true); + } + else + { + cx().trace(context::generic, " . {} (skipping, not tracked)", rp); + } + } +} + +void git::set_config(const std::string& key, const std::string& value) +{ + process_ = process() + .binary(binary()) + .arg("config") + .arg(key) + .arg(value) + .cwd(where_); + + execute_and_join(); +} + +bool git::has_remote(const std::string& name) +{ + process_ = process() + .binary(binary()) + .flags(process::allow_failure) + .stderr_level(context::level::debug) + .arg("remote") + .arg("show") + .arg(name) + .cwd(where_); + + return (execute_and_join() == 0); +} + +void git::rename_remote(const std::string& from, const std::string& to) +{ + process_ = process() + .binary(binary()) + .arg("remote") + .arg("rename") + .arg(from) + .arg(to) + .cwd(where_); + + execute_and_join(); +} + +void git::add_remote(const std::string& name, const std::string& url) +{ + process_ = process() + .binary(git::binary()) + .arg("remote") + .arg("add") + .arg(name) + .arg(url) + .cwd(where_); + + execute_and_join(); +} + +void git::set_remote_push(const std::string& remote, const std::string& url) +{ + process_ = process() + .binary(git::binary()) + .arg("remote") + .arg("set-url") + .arg("--push") + .arg(remote) + .arg(url) + .cwd(where_); + + execute_and_join(); +} + +void git::set_assume_unchanged(const fs::path& relative_file, bool on) +{ + process_ = process() + .binary(git::binary()) + .arg("update-index") + .arg(on ? "--assume-unchanged" : "--no-assume-unchanged") + .arg(relative_file, process::forward_slashes) + .cwd(where_); + + execute_and_join(); +} + +bool git::is_tracked(const fs::path& relative_file) +{ + process_ = process() + .binary(git::binary()) + .stdout_level(context::level::debug) + .stderr_level(context::level::debug) + .flags(process::allow_failure) + .arg("ls-files") + .arg("--error-unmatch") + .arg(relative_file, process::forward_slashes) + .cwd(where_); + + return (execute_and_join() == 0); +} + +std::string git::git_file() +{ + process_ = process() + .binary(git::binary()) + .stdout_flags(process::keep_in_string) + .arg("remote") + .arg("get-url") + .arg("origin") + .cwd(where_); + + execute_and_join(); + + const std::string out = process_.stdout_string(); + + const auto last_slash = out.find_last_of("/"); + if (last_slash == std::string::npos) + { + u8cerr << "bad get-url output '" << out << "'\n"; + throw bailed(); + } + + auto s = trim_copy(out.substr(last_slash + 1)); + + if (s.empty()) + { + u8cerr << "bad get-url output '" << out << "'\n"; + throw bailed(); + } + + return s; +} + +std::string git::make_url( + const std::string& username, const std::string& git_file) +{ + return "git@github.com:" + username + "/" + git_file; +} + } // namespace diff --git a/src/tools/patcher.cpp b/src/tools/patcher.cpp index 55833d9..c9ea11b 100644 --- a/src/tools/patcher.cpp +++ b/src/tools/patcher.cpp @@ -40,7 +40,7 @@ void patcher::do_run() if (!fs::exists(patches_root)) { - cx_->trace(context::generic, + cx().trace(context::generic, "patch directory {} doesn't exist, assuming no patches", patches_root); @@ -52,11 +52,11 @@ void patcher::do_run() const fs::path patches = patches_root / (prebuilt_ ? "prebuilt" : "sources"); - cx_->trace(context::generic, "looking for patches in {}", patches); + cx().trace(context::generic, "looking for patches in {}", patches); if (!fs::exists(patches)) { - cx_->trace(context::generic, + cx().trace(context::generic, "patch directory {} doesn't exist, assuming no patches", patches); @@ -67,7 +67,7 @@ void patcher::do_run() { if (!e.is_regular_file()) { - cx_->trace(context::generic, + cx().trace(context::generic, "skipping {}, not a file", e.path()); continue; @@ -77,14 +77,14 @@ void patcher::do_run() if (p.extension() == ".manual_patch") { - cx_->trace(context::generic, + cx().trace(context::generic, "skipping manual patch {}", e.path()); continue; } else if (p.extension() != ".patch") { - cx_->warning(context::generic, + cx().warning(context::generic, "file with unknown extension {}", p); continue; @@ -95,7 +95,7 @@ void patcher::do_run() } else { - cx_->trace(context::generic, "doing manual patch from {}", file_); + cx().trace(context::generic, "doing manual patch from {}", file_); do_patch(patches_root / file_); } } @@ -121,12 +121,12 @@ void patcher::do_patch(const fs::path& patch_file) .arg("--batch") .arg("--input", patch_file); - cx_->trace(context::generic, "trying to patch using {}", patch_file); + cx().trace(context::generic, "trying to patch using {}", patch_file); { // check - cx_->trace(context::generic, + cx().trace(context::generic, "checking if already patched"); process_ = check; @@ -134,26 +134,26 @@ void patcher::do_patch(const fs::path& patch_file) if (ret == 0) { - cx_->trace(context::generic, + cx().trace(context::generic, "patch {} already applied", patch_file); return; } else if (ret == 1) { - cx_->trace(context::generic, + cx().trace(context::generic, "looks like the patch is needed"); } else { - cx_->bail_out(context::generic, "patch returned {}", ret); + cx().bail_out(context::generic, "patch returned {}", ret); } } { // apply - cx_->trace(context::generic, "applying patch {}", patch_file); + cx().trace(context::generic, "applying patch {}", patch_file); process_ = apply; execute_and_join(); } diff --git a/src/tools/process_runner.cpp b/src/tools/process_runner.cpp index acd377b..02f6cc7 100644 --- a/src/tools/process_runner.cpp +++ b/src/tools/process_runner.cpp @@ -22,7 +22,7 @@ void basic_process_runner::do_interrupt() int basic_process_runner::execute_and_join() { - process_.set_context(cx_); + process_.set_context(&cx()); process_.run(); join(); @@ -72,7 +72,7 @@ void process_runner::do_interrupt() int process_runner::execute_and_join() { - real_process().set_context(cx_); + real_process().set_context(&cx()); real_process().run(); join(); diff --git a/src/tools/tools.cpp b/src/tools/tools.cpp index 42ded9d..bd58430 100644 --- a/src/tools/tools.cpp +++ b/src/tools/tools.cpp @@ -20,6 +20,7 @@ tool::tool(tool&& t) tool& tool::operator=(tool&& t) { + cx_ = t.cx_; name_ = std::move(t.name_); interrupted_ = t.interrupted_.load(); return *this; @@ -48,7 +49,7 @@ void tool::interrupt() { if (!interrupted_) { - cx_->debug(context::interruption, "interrupting {}", name_); + cx().debug(context::interruption, "interrupting {}", name_); interrupted_ = true; do_interrupt(); } @@ -59,6 +60,14 @@ bool tool::interrupted() const return interrupted_; } +const context& tool::cx() const +{ + if (cx_) + return *cx_; + else + return gcx(); +} + fs::path perl::binary() { @@ -154,7 +163,7 @@ void vs::do_run() default: { - cx_->bail_out(context::generic, "vs unknown op {}", op_); + cx().bail_out(context::generic, "vs unknown op {}", op_); } } } diff --git a/src/tools/tools.h b/src/tools/tools.h index 4a4f0cc..fa46299 100644 --- a/src/tools/tools.h +++ b/src/tools/tools.h @@ -25,10 +25,10 @@ public: void result() {} protected: - context* cx_; - tool(std::string name); + const context& cx() const; + bool interrupted() const; virtual void do_run() = 0; @@ -36,6 +36,7 @@ protected: virtual std::string do_name() const { return {}; } private: + context* cx_; std::string name_; std::atomic interrupted_; }; @@ -136,7 +137,13 @@ public: { clone = 1, pull, - clone_or_pull2 + clone_or_pull + }; + + struct creds + { + std::string username; + std::string email; }; @@ -144,9 +151,32 @@ public: static fs::path binary(); + static void set_credentials( + const fs::path& repo, + const std::string& username, const std::string& email); + + static void set_remote( + const fs::path& repo, + std::string username, std::string key, + bool no_push_upstream, bool push_default_origin); + + static void ignore_ts(const fs::path& repo, bool b); + + static void add_remote( + const fs::path& repo, const std::string& remote_name, + const std::string& username, const std::string& key, + bool push_default); + + git& url(const mob::url& u); git& branch(const std::string& name); git& output(const fs::path& dir); + git& credentials(const std::string& username, const std::string& email); + git& ignore_ts(bool b); + + git& remote( + std::string username, std::string key, + bool no_push_upstream, bool push_default_origin); protected: void do_run() override; @@ -156,10 +186,33 @@ private: mob::url url_; std::string branch_; fs::path where_; + std::string creds_username_; + std::string creds_email_; + std::string remote_username_; + std::string remote_key_; + bool no_push_upstream_ = false; + bool push_default_origin_ = false; + bool ignore_ts_; void do_clone_or_pull(); bool do_clone(); void do_pull(); + + void do_set_credentials(); + void do_set_remote(); + void do_ignore_ts(); + + void set_config(const std::string& key, const std::string& value); + bool has_remote(const std::string& name); + void rename_remote(const std::string& from, const std::string& to); + void add_remote(const std::string& name, const std::string& url); + void set_remote_push(const std::string& remote, const std::string& url); + void set_assume_unchanged(const fs::path& relative_file, bool on); + bool is_tracked(const fs::path& relative_file); + std::string git_file(); + + static std::string make_url( + const std::string& username, const std::string& git_file); };