diff --git a/src/cmd/commands.cpp b/src/cmd/commands.cpp index b4b1ad3..0de43c8 100644 --- a/src/cmd/commands.cpp +++ b/src/cmd/commands.cpp @@ -13,7 +13,9 @@ namespace mob BOOL WINAPI signal_handler(DWORD) noexcept { - gcx().debug(context::generic, "caught sigint"); + // don't use u8cout, this would lock the global mutex, but the handler + // can be called while stuff is being output and the mutex is locked + std::wcout << L"sigint, interrupting...\n"; task_manager::instance().interrupt_all(); return TRUE; } diff --git a/src/cmd/pr.cpp b/src/cmd/pr.cpp index ea33a1c..5f56957 100644 --- a/src/cmd/pr.cpp +++ b/src/cmd/pr.cpp @@ -397,7 +397,7 @@ std::vector pr_command::validate_prs( return {}; } - if (!ask_yes_no("these prs will be ignored; proceed anyway?", false)) + if (ask_yes_no("these prs will be ignored; proceed anyway?", yn::no) != yn::yes) return {}; u8cout << "\n"; diff --git a/src/cmd/release.cpp b/src/cmd/release.cpp index f2994b2..9aa0cbd 100644 --- a/src/cmd/release.cpp +++ b/src/cmd/release.cpp @@ -4,6 +4,7 @@ #include "../core/conf.h" #include "../core/context.h" #include "../core/op.h" +#include "../core/ini.h" #include "../tasks/tasks.h" #include "../tasks/task_manager.h" #include "../utility/threading.h" @@ -360,21 +361,45 @@ bool release_command::check_clean_prefix() if (!fs::exists(prefix)) return true; - u8cout - << "prefix " << path_to_utf8(prefix) << " already exists\n" - << "delete? [Y/n] "; + bool saw_file = false; + const fs::path log_file = conf().global().get("log_file"); + const std::string ini_file = default_ini_filename(); - std::wstring s; - std::getline(std::wcin, s); - - if (s == L"" || s == L"y" || s == L"Y") + for (auto itor : fs::directory_iterator(prefix)) { - build_command::terminate_msbuild(); - op::delete_directory(gcx(), prefix); + const auto name = itor.path().filename(); + + // ignore ini and logs + if (name == log_file.filename() || name == ini_file) + continue; + + saw_file = true; + break; + } + + if (!saw_file) + { + // empty directory, that's fine return true; } - return false; + const auto q = fmt::format( + "prefix {} already exists, delete?", path_to_utf8(prefix)); + + if (ask_yes_no(q, yn::no) != yn::yes) + return false; + + // the log file might be in this directory, close it now and reopen it + // when deletion is finished + context::close_log_file(); + + build_command::terminate_msbuild(); + op::delete_directory(gcx(), prefix); + + // reopen log file + conf().set_log_file(); + + return true; } void release_command::prepare() diff --git a/src/core/conf.cpp b/src/core/conf.cpp index fb20f92..dee8b19 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -401,6 +401,13 @@ void process_option( // task must exist const auto& tasks = task_manager::instance().find(task); + + if (tasks.empty()) + { + gcx().bail_out(context::conf, + "bad option {}, task '{}' not found", section_string, task); + } + MOB_ASSERT(!tasks.empty()); for (auto& t : tasks) @@ -522,6 +529,14 @@ void resolve_paths() details::set_string("tools", "iscc", path_to_utf8(find_iscc())); } +void conf::set_log_file() +{ + // set up the log file, resolve against prefix if relative + fs::path log_file = conf().global().get("log_file"); + if (log_file.is_relative()) + log_file = conf().path().prefix() / log_file; +} + void init_options( const std::vector& inis, const std::vector& opts) { @@ -596,11 +611,7 @@ void init_options( resolve_path("prefix", prefix_root, ""); // set up the log file, resolve against prefix if relative - fs::path log_file = conf().global().get("log_file"); - if (log_file.is_relative()) - log_file = conf().path().prefix() / log_file; - - context::set_log_file(log_file); + conf().set_log_file(); // goes through all paths and tools, finds missing or relative stuff, bails // out of stuff can't be found diff --git a/src/core/conf.h b/src/core/conf.h index 0dd82b7..2bd1e0e 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -242,6 +242,10 @@ public: conf_prebuilt prebuilt(); conf_versions version(); conf_paths path(); + + // opens the log file, creates the directory if needed + // + void set_log_file(); }; } // namespace diff --git a/src/core/context.cpp b/src/core/context.cpp index efe9a6d..a628820 100644 --- a/src/core/context.cpp +++ b/src/core/context.cpp @@ -248,6 +248,11 @@ void context::set_log_file(const fs::path& p) } } +void context::close_log_file() +{ + g_log_file.reset(); +} + void context::log_string(reason r, level lv, std::string_view s) const { if (!enabled(lv)) diff --git a/src/core/context.h b/src/core/context.h index 4306dad..6a38141 100644 --- a/src/core/context.h +++ b/src/core/context.h @@ -148,6 +148,10 @@ public: // static void set_log_file(const fs::path& p); + // closes the output file for logs, see release_command::check_clean_prefix() + // + static void close_log_file(); + // creates a context for a task; the global context has no name // diff --git a/src/core/ini.h b/src/core/ini.h index 2130b60..4957078 100644 --- a/src/core/ini.h +++ b/src/core/ini.h @@ -27,5 +27,6 @@ struct ini_data }; ini_data parse_ini(const fs::path& ini); +std::string default_ini_filename(); } // namespace diff --git a/src/utility/assert.cpp b/src/utility/assert.cpp index 8434ace..96c2501 100644 --- a/src/utility/assert.cpp +++ b/src/utility/assert.cpp @@ -9,21 +9,23 @@ void mob_assertion_failed( const char* message, const char* exp, const wchar_t* file, int line, const char* func) { - if (message) - { - gcx().error(context::generic, - "assertion failed: {}:{} {}: {} ({})", - std::wstring(file), line, func, message, exp); - } - else - { - gcx().error(context::generic, - "assertion failed: {}:{} {}: '{}'", - std::wstring(file), line, func, exp); - } + if (message) + { + gcx().error(context::generic, + "assertion failed: {}:{} {}: {} ({})", + std::wstring(file), line, func, message, exp); + } + else + { + gcx().error(context::generic, + "assertion failed: {}:{} {}: '{}'", + std::wstring(file), line, func, exp); + } - if (IsDebuggerPresent()) - DebugBreak(); + if (IsDebuggerPresent()) + DebugBreak(); + else + std::exit(1); } } // namespace diff --git a/src/utility/io.cpp b/src/utility/io.cpp index dd9304c..9ff3c2f 100644 --- a/src/utility/io.cpp +++ b/src/utility/io.cpp @@ -81,65 +81,71 @@ std::mutex& global_output_mutex() return g_output_mutex; } -bool ask_yes_no(const std::string& text, bool def) +yn ask_yes_no(const std::string& text, yn def) { u8cout << text << (text.empty() ? "" : " ") - << (def ? "[Y/n]" : "[y/N]") + << (def == yn::yes ? "[Y/n]" : "[y/N]") << " "; // stdin is not utf8 std::string line; std::getline(std::cin, line); + // ctrl+c + if (!std::cin) + return yn::cancelled; + if (line.empty()) return def; else if (line == "y" || line == "Y") - return true; + return yn::yes; + else if (line == "n" || line == "N") + return yn::no; else - return false; + return yn::cancelled; } void u8stream::do_output(const std::string& s) { - std::scoped_lock lock(g_output_mutex); + std::scoped_lock lock(g_output_mutex); - if (err_) - { - if (stderr_console) - std::wcerr << utf8_to_utf16(s); + if (err_) + { + if (stderr_console) + std::wcerr << utf8_to_utf16(s); + else + std::cerr << s; + } else - std::cerr << s; - } - else - { - if (stdout_console) - std::wcout << utf8_to_utf16(s); - else - std::cout << s; - } + { + if (stdout_console) + std::wcout << utf8_to_utf16(s); + else + std::cout << s; + } } void u8stream::write_ln(std::string_view utf8) { - std::scoped_lock lock(g_output_mutex); + std::scoped_lock lock(g_output_mutex); - if (err_) - { - if (stderr_console) - std::wcerr << utf8_to_utf16(utf8) << L"\n"; + if (err_) + { + if (stderr_console) + std::wcerr << utf8_to_utf16(utf8) << L"\n"; + else + std::cerr << utf8 << "\n"; + } else - std::cerr << utf8 << "\n"; - } - else - { - if (stdout_console) - std::wcout << utf8_to_utf16(utf8) << L"\n"; - else - std::cout << utf8 << "\n"; - } + { + if (stdout_console) + std::wcout << utf8_to_utf16(utf8) << L"\n"; + else + std::cout << utf8 << "\n"; + } } diff --git a/src/utility/io.h b/src/utility/io.h index c3c6b80..2f21a5d 100644 --- a/src/utility/io.h +++ b/src/utility/io.h @@ -102,9 +102,17 @@ void set_std_streams(); // std::mutex& global_output_mutex(); + +enum class yn +{ + no = 0, + yes, + cancelled +}; + // asks the user for y/n // -bool ask_yes_no(const std::string& text, bool def); +yn ask_yes_no(const std::string& text, yn def); // see https://github.com/isanae/mob/issues/4 diff --git a/src/utility/threading.cpp b/src/utility/threading.cpp index 61dd029..6a4d2b1 100644 --- a/src/utility/threading.cpp +++ b/src/utility/threading.cpp @@ -19,9 +19,9 @@ static LPTOP_LEVEL_EXCEPTION_FILTER g_previous_handler = nullptr; void dump_stacktrace(const wchar_t* what) { - std::scoped_lock lock(global_output_mutex()); - - std::wcerr + // don't use 8ucout, don't lock the global out mutex, this can be called + // while the mutex is locked + std::wcerr << what << "\n\nmob has crashed\n" << L"*****************************\n\n" << what << L"\n\n";