diff --git a/src/conf.cpp b/src/conf.cpp index 4cd7e51..40d0c79 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -128,10 +128,7 @@ const typename Map::mapped_type& get( auto itor = map.find(name); if (itor == map.end()) - { - gcx().bail_out(context::conf, - map_name + " '" + name + "' doesn't exist"); - } + gcx().bail_out(context::conf, "{} '{}' doesn't exist", map_name, name); return itor->second; } @@ -174,10 +171,7 @@ const fs::path& paths::by_name(const std::string& s) void conf::set_output_log_level(int i) { if (i < 0 || i > 6) - { - gcx().bail_out(context::generic, - "bad output log level " + std::to_string(i)); - } + gcx().bail_out(context::generic, "bad output log level {}", i); g_output_log_level = i; } @@ -185,10 +179,7 @@ void conf::set_output_log_level(int i) void conf::set_file_log_level(int i) { if (i < 0 || i > 6) - { - gcx().bail_out(context::generic, - "bad file log level " + std::to_string(i)); - } + gcx().bail_out(context::generic, "bad file log level {}", i); g_file_log_level = i; } @@ -263,13 +254,13 @@ bool set_option_impl( auto itor = map.find(key); if (itor == map.end()) { - gcx().error(context::conf, "unknown key '" + key + "'"); + gcx().error(context::conf, "unknown key '{}'", key); return false; } if (!parse_value(value, itor->second)) { - gcx().error(context::conf, "bad value '" + value + "'"); + gcx().error(context::conf, "bad value '{}'", value); return false; } @@ -291,7 +282,7 @@ bool set_option( else if (section == "paths") return set_option_impl(g_paths, key, value); - gcx().error(context::conf, "bad section name '" + section + "'"); + gcx().error(context::conf, "bad section name '{}'", section); return false; } @@ -301,14 +292,14 @@ void set_option(const std::string& s) if (slash == std::string::npos) { gcx().bail_out(context::conf, - "bad option " + s + ", must be section/key=value"); + "bad option {}, must be section/key=value", s); } const auto equal = s.find("=", slash); if (slash == std::string::npos) { gcx().bail_out(context::conf, - "bad option " + s + ", must be section/key=value"); + "bad option {}, must be section/key=value", s); } const std::string section = s.substr(0, slash); @@ -317,13 +308,12 @@ void set_option(const std::string& s) if (set_option(section, key, value)) { - gcx().trace(context::conf, - "setting " + section + "/" + key + "=" + value); + gcx().trace(context::conf, "setting {}/{}={}", section, key, value); } else { gcx().bail_out(context::conf, - "failed to set " + section + "/" + key + "=" + value); + "failed to set {}/{}={}", section, key, value); } } @@ -354,7 +344,7 @@ bool try_parts(fs::path& check, const std::vector& parts) for (std::size_t j=i; j(buffer_size), buffer) == 0) { const auto e = GetLastError(); - gcx().bail_out(context::conf, "can't get temp path", e); + gcx().bail_out(context::conf, "can't get temp path", error_message(e)); } fs::path p(buffer); - gcx().trace(context::conf, "temp dir is " + p.string()); + gcx().trace(context::conf, "temp dir is {}", p); return p; } @@ -608,12 +590,12 @@ fs::path find_vs() if (p.exit_code() != 0) gcx().bail_out(context::conf, "vswhere failed"); - fs::path path = trim_copy(p.steal_stdout()); + fs::path path = trim_copy(p.stdout_string()); if (!fs::exists(path)) { gcx().bail_out(context::conf, - "the path given by vswhere doesn't exist: " + path.string()); + "the path given by vswhere doesn't exist: {}", path); } return path; @@ -647,24 +629,23 @@ void find_vcvars() / "VC" / "Auxiliary" / "Build" / "vcvarsall.bat"; if (!try_vcvars(bat)) - gcx().bail_out(context::conf, "vcvars not found " + bat.string()); + gcx().bail_out(context::conf, "vcvars not found at {}", bat); } else { if (!try_vcvars(bat)) - gcx().bail_out(context::conf, "vcvars not found " + bat.string()); + gcx().bail_out(context::conf, "vcvars not found at {}", bat); } - gcx().trace(context::conf, "using vcvars at " + bat.string()); + gcx().trace(context::conf, "using vcvars at {}", bat); } void ini_error(std::size_t line, const std::string& what) { gcx().bail_out(context::conf, - g_ini.filename().string() + ":" + - std::to_string(line + 1) + ": " + - what); + "{}:{}: {}", + g_ini.filename(), (line + 1), what); } fs::path find_ini(const fs::path& ini) @@ -676,7 +657,7 @@ fs::path find_ini(const fs::path& ini) if (fs::exists(p)) return fs::canonical(p); else - gcx().bail_out(context::conf, "can't find ini at " + ini.string()); + gcx().bail_out(context::conf, "can't find ini at {}", ini); } p = fs::current_path(); @@ -684,7 +665,7 @@ fs::path find_ini(const fs::path& ini) if (try_parts(p, {"..", "..", "..", default_ini_filename})) return p; - gcx().bail_out(context::conf, "can't find " + default_ini_filename); + gcx().bail_out(context::conf, "can't find {}", default_ini_filename); } std::vector read_ini(const fs::path& ini) @@ -709,7 +690,7 @@ std::vector read_ini(const fs::path& ini) } if (in.bad()) - gcx().bail_out(context::conf, "failed to read ini " + ini.string()); + gcx().bail_out(context::conf, "failed to read ini {}", ini); return lines; } @@ -747,7 +728,7 @@ void parse_section( void parse_ini(const fs::path& ini) { g_ini = find_ini(ini); - gcx().debug(context::conf, "using ini at " + g_ini.string()); + gcx().debug(context::conf, "using ini at {}", g_ini); const auto lines = read_ini(g_ini); std::size_t i = 0; @@ -815,7 +796,7 @@ void set_path_if_empty(const std::string& k, F&& f) { auto itor = g_paths.find(k); if (itor == g_paths.end()) - gcx().bail_out(context::conf, "unknown path key " + k); + gcx().bail_out(context::conf, "unknown path key {}", k); if (!itor->second.empty()) { @@ -829,8 +810,7 @@ void set_path_if_empty(const std::string& k, F&& f) } else { - gcx().bail_out(context::conf, - "path " + itor->second.string() + " not found"); + gcx().bail_out(context::conf, "path {} not found", itor->second); } } @@ -850,10 +830,7 @@ void set_path_if_empty(const std::string& k, F&& f) cp = fs::absolute(cp); if (!fs::exists(cp)) - { - gcx().bail_out(context::conf, - "path " + cp.string() + " not found"); - } + gcx().bail_out(context::conf, "path {} not found", cp); itor->second = fs::canonical(cp); } @@ -864,7 +841,7 @@ void make_canonical_path( { auto itor = g_paths.find(key); if (itor == g_paths.end()) - gcx().bail_out(context::conf, "unknown path key " + key); + gcx().bail_out(context::conf, "unknown path key {}", key); if (itor->second.empty()) { @@ -961,9 +938,9 @@ void table(const std::string& caption, const Map& values) for (auto&& [k, v] : values) longest = std::max(longest, k.size()); - gcx().trace(context::conf, caption + ":"); + gcx().trace(context::conf, "{}:", caption); for (auto&& [k, v] : values) - gcx().trace(context::conf, " . " + pad_right(k, longest) + " = " + v); + gcx().trace(context::conf, " . {} = {}", pad_right(k, longest), v); } void dump_options() @@ -1000,7 +977,7 @@ fs::path make_temp_file() const auto e = GetLastError(); gcx().bail_out(context::conf, - "can't create temp file in " + dir.string(), e); + "can't create temp file in {}, {}", dir, error_message(e)); } return dir / name; diff --git a/src/context.cpp b/src/context.cpp index e1cdcc5..1d3967e 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -5,6 +5,27 @@ #include "tasks/task.h" #include "tools/tools.h" +namespace mob::details +{ + +std::string converter::convert(const std::wstring& s) +{ + return utf16_to_utf8(s); +} + +std::string converter::convert(const fs::path& s) +{ + return utf16_to_utf8(s.native()); +} + +std::string converter::convert(const url& u) +{ + return u.string(); +} + +} // namespace + + namespace mob { @@ -183,77 +204,57 @@ void context::set_log_file(const fs::path& p) if (h == INVALID_HANDLE_VALUE) { const auto e = GetLastError(); - gcx().bail_out( - context::generic, "failed to open log file " + p.string(), e); + gcx().bail_out(context::generic, + "failed to open log file {}, {}", p, error_message(e)); } g_log_file.reset(h); } } -void context::log(reason r, level lv, std::string_view s) const +void context::do_log_impl( + bool bail, reason r, level lv, const std::string& utf8) const { - if (!enabled(lv)) + if (!bail && !enabled(lv)) return; - const auto ls = make_log_string(r, lv, s); - do_log(lv, ls); -} + const auto ls = make_log_string(r, lv, utf8); -void context::log(reason r, level lv, std::string_view s, DWORD e) const -{ - log(r, lv, std::string(s) + ", " + error_message(e)); -} - -void context::log(reason r, level lv, std::string_view s, const std::error_code& ec) const -{ - log(r, lv, std::string(s) + ", " + ec.message()); -} - -void context::bail_out(reason r, std::string_view s) const -{ - const auto ls = make_log_string(r, level::error, s); - do_log(level::error, ls + " (bailing out)"); - - throw bailed(ls); -} - -void context::bail_out(reason r, std::string_view s, DWORD e) const -{ - bail_out(r, std::string(s) + ", " + error_message(e)); -} - -void context::bail_out(reason r, std::string_view s, const std::error_code& ec) const -{ - bail_out(r, std::string(s) + ", " + ec.message()); -} - -void context::do_log(level lv, const std::string& s) const -{ + if (bail) { - std::scoped_lock lock(g_mutex); + emit_log(lv, ls + " (bailing out)"); + throw bailed(ls); + } + else + { + emit_log(lv, ls); + } +} - if (lv == level::error) - g_errors.push_back(s); - else if (lv == level::warning) - g_warnings.push_back(s); +void context::emit_log(level lv, const std::string& utf8) const +{ + std::scoped_lock lock(g_mutex); - if (log_enabled(lv, conf::output_log_level())) - { - auto c = level_color(lv); - u8cout << s << "\n"; - } + if (lv == level::error) + g_errors.push_back(utf8); + else if (lv == level::warning) + g_warnings.push_back(utf8); - if (g_log_file && log_enabled(lv, conf::file_log_level())) - { - DWORD written = 0; + if (log_enabled(lv, conf::output_log_level())) + { + u8cout << utf8 << "\n"; + auto c = level_color(lv); + } - ::WriteFile( - g_log_file.get(), s.c_str(), static_cast(s.size()), - &written, nullptr); + if (g_log_file && log_enabled(lv, conf::file_log_level())) + { + DWORD written = 0; - ::WriteFile(g_log_file.get(), "\r\n", 2, &written, nullptr); - } + ::WriteFile( + g_log_file.get(), utf8.c_str(), static_cast(utf8.size()), + &written, nullptr); + + ::WriteFile(g_log_file.get(), "\r\n", 2, &written, nullptr); } } diff --git a/src/context.h b/src/context.h index cb8f300..9eae78c 100644 --- a/src/context.h +++ b/src/context.h @@ -1,11 +1,68 @@ #pragma once +#include "utility.h" + +namespace mob +{ + class url; +} + + +namespace mob::details +{ + +// T to std::string converters +// +// those are kept in this namespace so they don't leak all over the place; +// they're used directly by doLog() below + +template +struct converter +{ + static const T& convert(const T& t) + { + return t; + } +}; + +template <> +struct converter +{ + static std::string convert(const std::wstring& s); +}; + +template <> +struct converter +{ + static std::string convert(const fs::path& s); +}; + +template <> +struct converter +{ + static std::string convert(const url& u); +}; + +template +struct converter>> +{ + static std::string convert(T e) + { + return std::to_string(static_cast>(e)); + } +}; + +} // namespace + + namespace mob { class task; class tool; +std::string error_message(DWORD e); + class context { public: @@ -65,56 +122,79 @@ public: void set_tool(tool* t); - void log(reason r, level lv, std::string_view s) const; - void log(reason r, level lv, std::string_view s, DWORD e) const; - void log(reason r, level lv, std::string_view s, const std::error_code& ec) const; - template - void dump(reason r, std::string_view s, Args&&... args) const + void log(reason r, level lv, const char* f, Args&&... args) const { - log(r, level::dump, s, std::forward(args)...); + do_log(false, r, lv, f, std::forward(args)...); } template - void trace(reason r, std::string_view s, Args&&... args) const + void dump(reason r, const char* f, Args&&... args) const { - log(r, level::trace, s, std::forward(args)...); + do_log(false, r, level::dump, f, std::forward(args)...); } template - void debug(reason r, std::string_view s, Args&&... args) const + void trace(reason r, const char* f, Args&&... args) const { - log(r, level::debug, s, std::forward(args)...); + do_log(false, r, level::trace, f, std::forward(args)...); } template - void info(reason r, std::string_view s, Args&&... args) const + void debug(reason r, const char* f, Args&&... args) const { - log(r, level::info, s, std::forward(args)...); + do_log(false, r, level::debug, f, std::forward(args)...); } template - void warning(reason r, std::string_view s, Args&&... args) const + void info(reason r, const char* f, Args&&... args) const { - log(r, level::warning, s, std::forward(args)...); + do_log(false, r, level::info, f, std::forward(args)...); } template - void error(reason r, std::string_view s, Args&&... args) const + void warning(reason r, const char* f, Args&&... args) const { - log(r, level::error, s, std::forward(args)...); + do_log(false, r, level::warning, f, std::forward(args)...); } - [[noreturn]] void bail_out(reason r, std::string_view s) const; - [[noreturn]] void bail_out(reason r, std::string_view s, DWORD e) const; - [[noreturn]] void bail_out(reason r, std::string_view s, const std::error_code& ec) const; + template + void error(reason r, const char* f, Args&&... args) const + { + do_log(false, r, level::error, f, std::forward(args)...); + } + + template + [[noreturn]] void bail_out(reason r, const char* f, Args&&... args) const + { + do_log(true, r, level::error, f, std::forward(args)...); + } private: std::string task_; const tool* tool_; + template + void do_log(bool bail, reason r, level lv, const char* f, Args&&... args) const + { + try + { + const std::string utf8 = fmt::format( + f, + details::converter>::convert( + std::forward(args))...); + + do_log_impl(bail, r, lv, utf8); + } + catch(std::exception&) + { + MOB_ASSERT(false, "bad format string"); + } + } + std::string make_log_string(reason r, level lv, std::string_view s) const; - void do_log(level lv, const std::string& s) const; + void do_log_impl(bool bail, reason r, level lv, const std::string& utf8) const; + void emit_log(level lv, const std::string& utf8) const; }; @@ -130,17 +210,17 @@ void dump_logs(); inline void out(context::level lv, const std::string& s) { - gcx().log(context::generic, lv, s); + gcx().log(context::generic, lv, "{}", s); } inline void out(context::level lv, const std::string& s, DWORD e) { - gcx().log(context::generic, lv, s, e); + gcx().log(context::generic, lv, "{}, {}", s, error_message(e)); } inline void out(context::level lv, const std::string& s, const std::error_code& ec) { - gcx().log(context::generic, lv, s, ec); + gcx().log(context::generic, lv, "{}, {}", s, ec.message()); } template diff --git a/src/env.cpp b/src/env.cpp index d30fe00..8d156b5 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -27,7 +27,7 @@ env get_vcvars_env(arch a) gcx().bail_out(context::generic, "get_vcvars_env: bad arch"); } - gcx().trace(context::generic, "looking for vcvars for " + arch_s); + gcx().trace(context::generic, "looking for vcvars for {}", arch_s); const fs::path tmp = make_temp_file(); @@ -39,7 +39,7 @@ env get_vcvars_env(arch a) process::raw(gcx(), cmd) .run(); - gcx().trace(context::generic, "reading from " + tmp.string()); + gcx().trace(context::generic, "reading from {}", tmp); std::stringstream ss(op::read_text_file(gcx(), tmp)); op::delete_file(gcx(), tmp); @@ -63,7 +63,7 @@ env get_vcvars_env(arch a) std::string name = line.substr(0, sep); std::string value = line.substr(sep + 1); - gcx().trace(context::generic, name + " = " + value); + gcx().trace(context::generic, "{} = {}", name, value); e.set(std::move(name), std::move(value)); } @@ -225,7 +225,7 @@ void this_env::set(const std::string& k, const std::string& v, env::flags f) void this_env::prepend_to_path(const fs::path& p) { - gcx().trace(context::generic, "prepending to PATH: " + p.string()); + gcx().trace(context::generic, "prepending to PATH: {}", p); set("PATH", p.string() + ";", env::prepend); } @@ -235,7 +235,7 @@ std::string this_env::get(const std::string& name) name.c_str(), nullptr, 0); if (buffer_size == 0) - bail_out("environment variable " + name + " doesn't exist"); + bail_out("environment variable {} doesn't exist", name); auto buffer = std::make_unique(buffer_size + 1); std::fill(buffer.get(), buffer.get() + buffer_size + 1, 0); diff --git a/src/main.cpp b/src/main.cpp index f37a8a1..fa9da3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -325,7 +325,7 @@ int main(int argc, char** argv) else { mob::gcx().debug(mob::context::generic, - "mob finished with exit code " + std::to_string(r)); + "mob finished with exit code {}", r); } mob::dump_logs(); diff --git a/src/net.cpp b/src/net.cpp index 0dafbf2..0631481 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -55,13 +55,13 @@ std::string url::filename() const auto r = curl_url_set(h, CURLUPART_URL, s_.c_str(), 0); if (r != CURLUE_OK) - gcx().bail_out(context::net, "bad url '" + s_ + "'"); + gcx().bail_out(context::net, "bad url '{}'", s_); char* buffer = nullptr; r = curl_url_get(h, CURLUPART_PATH, &buffer, 0); if (r != CURLUE_OK) - gcx().bail_out(context::net, "bad url '" + s_ + "'"); + gcx().bail_out(context::net, "bad url '{}'", s_); guard g2([&]{ curl_free(buffer); }); @@ -88,8 +88,7 @@ void curl_downloader::start(const url& u, const fs::path& path) path_ = path; ok_ = false; - cx_.debug(context::net, - "downloading " + url_.string() + " to " + path_.string()); + cx_.debug(context::net, "downloading {} to {}", url_, path_); if (conf::dry()) return; @@ -126,7 +125,7 @@ bool curl_downloader::ok() const void curl_downloader::run() { - cx_.trace(context::net, "curl: initializing " + url_.string()); + cx_.trace(context::net, "curl: initializing {}", url_); auto* c = curl_easy_init(); guard g([&]{ curl_easy_cleanup(c); }); @@ -153,9 +152,9 @@ void curl_downloader::run() file_deleter output_deleter(cx_, path_); - cx_.trace(context::net, "curl: performing " + url_.string()); + cx_.trace(context::net, "curl: performing {}", url_); const auto r = curl_easy_perform(c); - cx_.trace(context::net, "curl: transfer finished " + url_.string()); + cx_.trace(context::net, "curl: transfer finished {}", url_); if (file_) { @@ -165,7 +164,7 @@ void curl_downloader::run() if (interrupt_) { - cx_.trace(context::net, "curl: " + url_.string() + " interrupted"); + cx_.trace(context::net, "curl: {} interrupted", url_); return; } @@ -177,24 +176,22 @@ void curl_downloader::run() if (h == 200) { cx_.trace(context::net, - "curl: http 200 " + url_.string() + ", " - "transferred " + std::to_string(bytes_) + " bytes"); + "curl: http 200 {}, transferred {} bytes", + url_, bytes_); ok_ = true; output_deleter.cancel(); } else { - cx_.error(context::net, - "curl: http " + std::to_string(h) + " " + url_.string()); + cx_.error(context::net, "curl: http {} {}", h, url_); } } else { cx_.error(context::net, - std::string("curl: ") + - curl_easy_strerror(r) + ", " + trim_copy(error_buffer) + " " + - "(" + url_.string() + ")"); + "curl: {}, {} {}", + curl_easy_strerror(r), trim_copy(error_buffer), url_); } } @@ -226,7 +223,7 @@ void curl_downloader::on_write(char* ptr, std::size_t n) noexcept { op::create_directories(cx_, path_.parent_path()); - cx_.trace(context::net, "opening " + path_.string()); + cx_.trace(context::net, "opening {}", path_); HANDLE h = ::CreateFileA( path_.string().c_str(), GENERIC_WRITE, FILE_SHARE_READ, @@ -235,7 +232,10 @@ void curl_downloader::on_write(char* ptr, std::size_t n) noexcept if (h == INVALID_HANDLE_VALUE) { const auto e = GetLastError(); - cx_.error(context::net, "failed to open " + path_.string(), e); + + cx_.error(context::net, + "failed to open {}, {}", path_, error_message(e)); + interrupt_ = true; return; } @@ -249,7 +249,10 @@ void curl_downloader::on_write(char* ptr, std::size_t n) noexcept if (!::WriteFile(file_.get(), ptr, static_cast(n), &written, nullptr)) { const auto e = GetLastError(); - cx_.error(context::net, "failed to write to " + path_.string(), e); + + cx_.error(context::net, + "failed to write to {}, {}", path_, error_message(e)); + interrupt_ = true; } } @@ -336,7 +339,7 @@ void curl_downloader::on_debug(curl_infotype type, std::string_view s) buffer.append(line); } - cx_.dump(context::net, buffer); + cx_.dump(context::net, "{}", buffer); }); }; diff --git a/src/op.cpp b/src/op.cpp index d167cd7..7f1fd69 100644 --- a/src/op.cpp +++ b/src/op.cpp @@ -20,7 +20,7 @@ void check(const context& cx, const fs::path& p); void touch(const context& cx, const fs::path& p) { - cx.trace(context::fs, "touching " + p.string()); + cx.trace(context::fs, "touching {}", p); check(cx, p); if (!conf::dry()) @@ -29,7 +29,7 @@ void touch(const context& cx, const fs::path& p) void create_directories(const context& cx, const fs::path& p) { - cx.trace(context::fs, "creating dir " + p.string()); + cx.trace(context::fs, "creating dir {}", p); check(cx, p); if (!conf::dry()) @@ -38,7 +38,7 @@ void create_directories(const context& cx, const fs::path& p) void delete_directory(const context& cx, const fs::path& p, flags f) { - cx.trace(context::fs, "deleting dir " + p.string()); + cx.trace(context::fs, "deleting dir {}", p); check(cx, p); if (!fs::exists(p)) @@ -46,18 +46,16 @@ void delete_directory(const context& cx, const fs::path& p, flags f) if (f & optional) { cx.trace(context::fs, - "not deleting dir " + p.string() + ", " - "doesn't exist (optional)"); + "not deleting dir {}, doesn't exist (optional)", p); return; } - cx.bail_out(context::fs, - "can't delete dir " + p.string() + ", doesn't exist"); + cx.bail_out(context::fs, "can't delete dir {}, doesn't exist", p); } if (fs::exists(p) && !fs::is_directory(p)) - cx.bail_out(context::fs, p.string() + " is not a dir"); + cx.bail_out(context::fs, "{} is not a dir", p); if (!conf::dry()) do_delete_directory(cx, p); @@ -65,7 +63,7 @@ void delete_directory(const context& cx, const fs::path& p, flags f) void delete_file(const context& cx, const fs::path& p, flags f) { - cx.trace(context::fs, "deleting file " + p.string()); + cx.trace(context::fs, "deleting file {}", p); check(cx, p); if (!fs::exists(p)) @@ -73,21 +71,16 @@ void delete_file(const context& cx, const fs::path& p, flags f) if (f & optional) { cx.trace(context::fs, - "not deleting file " + p.string() + ", " - "doesn't exist (optional)"); + "not deleting file {}, doesn't exist (optional)", p); return; } - cx.bail_out(context::fs, - "can't delete file " + p.string() + ", doesn't exist"); + cx.bail_out(context::fs, "can't delete file {}, doesn't exist", p); } if (fs::exists(p) && !fs::is_regular_file(p)) - { - cx.bail_out(context::fs, - "can't delete " + p.string() + ", not a file"); - } + cx.bail_out(context::fs, "can't delete {}, not a file", p); if (!conf::dry()) do_delete_file(cx, p); @@ -95,7 +88,7 @@ void delete_file(const context& cx, const fs::path& p, flags f) void remove_readonly(const context& cx, const fs::path& first) { - cx.trace(context::fs, "removing read-only from " + first.string()); + cx.trace(context::fs, "removing read-only from {}", first); check(cx, first); if (!conf::dry()) @@ -116,9 +109,7 @@ bool is_source_better( { if (!fs::exists(dest)) { - cx.trace(context::fs, - "target " + dest.string() + " doesn't exist; copying"); - + cx.trace(context::fs, "target {} doesn't exist; copying", dest); return true; } @@ -128,7 +119,8 @@ bool is_source_better( if (ec) { cx.warning(context::fs, - "failed to get size of " + src.string() + "; forcing copy"); + "failed to get size of {}, {}; forcing copy", + src, ec.message()); return true; } @@ -137,18 +129,17 @@ bool is_source_better( if (ec) { cx.warning(context::fs, - "failed to get size of " + dest.string() + "; forcing copy"); + "failed to get size of {}, {}; forcing copy", + dest, ec.message()); return true; } if (src_size != dest_size) { - cx.trace( - context::fs, - "src " + src.string() + " is " + std::to_string(src_size) + "), " - "dest " + dest.string() + " is " + std::to_string(dest_size) + "); " - "sizes different, copying"); + cx.trace(context::fs, + "src {} bytes, dest {} bytes; different, copying", + src, src_size, dest, dest_size); return true; } @@ -158,7 +149,8 @@ bool is_source_better( if (ec) { cx.warning(context::fs, - "failed to get time of " + src.string() + "; forcing copy"); + "failed to get time of {}, {}; forcing copy", + src, ec.message()); return true; } @@ -167,7 +159,8 @@ bool is_source_better( if (ec) { cx.warning(context::fs, - "failed to get time of " + dest.string() + "; forcing copy"); + "failed to get time of {}, {}; forcing copy", + dest, ec.message()); return true; } @@ -175,8 +168,8 @@ bool is_source_better( if (src_time > dest_time) { cx.trace(context::fs, - "src " + src.string() + " is newer than " + dest.string() + "; " - "copying"); + "src {} is newer than dest {}; copying", + src, dest); return true; } @@ -193,11 +186,10 @@ void rename(const context& cx, const fs::path& src, const fs::path& dest) if (fs::exists(dest)) { cx.bail_out(context::fs, - "can't rename " + src.string() + " to " + dest.string() + ", " - "already exists"); + "can't rename {} to {}, already exists", src, dest); } - cx.trace(context::fs, "renaming " + src.string() + " to " + dest.string()); + cx.trace(context::fs, "renaming {} to {}", src, dest); do_rename(cx, src, dest); } @@ -212,11 +204,11 @@ void move_to_directory( if (fs::exists(target)) { cx.bail_out(context::fs, - "can't move " + src.string() + " to " + dest_dir.string() + ", " + - src.filename().string() + " already exists"); + "can't move {} to directory {}, {} already exists", + src, dest_dir, target); } - cx.trace(context::fs, "moving " + src.string() + " to " + target.string()); + cx.trace(context::fs, "moving {} to {}", src, target); do_rename(cx, src, target); } @@ -230,7 +222,7 @@ void copy_file_to_dir_if_better( } if (file.string().find("*") != std::string::npos) - cx.bail_out(context::fs, file.string() + " contains a glob"); + cx.bail_out(context::fs, "{} contains a glob", file); if (!conf::dry()) { @@ -239,35 +231,29 @@ void copy_file_to_dir_if_better( if (f & optional) { cx.trace(context::fs, - "not copying " + file.string() + ", " - "doesn't exist (optional)"); + "not copying {}, doesn't exist (optional)", file); return; } - cx.bail_out(context::fs, - "can't copy " + file.string() + ", not a file"); + cx.bail_out(context::fs, "can't copy {}, not a file", file); } if (fs::exists(dir) && !fs::is_directory(dir)) - { - cx.bail_out(context::fs, - "can't copy to " + dir.string() + ", not a dir"); - } + cx.bail_out(context::fs, "can't copy to {}, not a dir", dir); } const auto target = dir / file.filename(); if (is_source_better(cx, file, target)) { - cx.trace(context::fs, file.string() + " -> " + dir.string()); + cx.trace(context::fs, "{} -> {}", file, dir); if (!conf::dry()) do_copy_file_to_dir(cx, file, dir); } else { - cx.trace(context::bypass, - "(skipped) " + file.string() + " -> " + dir.string()); + cx.trace(context::bypass, "(skipped) {} -> {}", file, dir); } } @@ -281,7 +267,7 @@ void copy_file_to_file_if_better( } if (src.string().find("*") != std::string::npos) - cx.bail_out(context::fs, src.string() + " contains a glob"); + cx.bail_out(context::fs, "{} contains a glob", src); if (!conf::dry()) { @@ -290,35 +276,31 @@ void copy_file_to_file_if_better( if (f & optional) { cx.trace(context::fs, - "not copying " + src.string() + ", " - "doesn't exist (optional)"); + "not copying {}, doesn't exist (optional)", src); return; } - cx.bail_out(context::fs, - "can't copy " + src.string() + ", doesn't exist"); + cx.bail_out(context::fs, "can't copy {}, doesn't exist", src); } if (fs::exists(dest) && fs::is_directory(dest)) { cx.bail_out(context::fs, - "can't copy to " + dest.string() + ", already exists but is " - "a directory"); + "can't copy to {}, already exists but is a directory", dest); } } if (is_source_better(cx, src, dest)) { - cx.trace(context::fs, src.string() + " -> " + dest.string()); + cx.trace(context::fs, "{} -> {}", src, dest); if (!conf::dry()) do_copy_file_to_file(cx, src, dest); } else { - cx.trace(context::bypass, - "(skipped) " + src.string() + " -> " + dest.string()); + cx.trace(context::bypass, "(skipped) {} -> {}", src, dest); } } @@ -336,7 +318,7 @@ void copy_glob_to_dir_if_better( if (!PathMatchSpecA(name.c_str(), wildcard.c_str())) { cx.trace(context::fs, - name + " did not match " + wildcard + "; skipping"); + "{} did not match {}; skipping", name, wildcard); continue; } @@ -350,8 +332,8 @@ void copy_glob_to_dir_if_better( else { cx.trace(context::fs, - "file " + name + " matched " + wildcard + " " - "but files are not copied"); + "file {} matched {} but files are not copied", + name, wildcard); } } else if (e.is_directory()) @@ -366,8 +348,8 @@ void copy_glob_to_dir_if_better( else { cx.trace(context::fs, - "directory " + name + " matched " + wildcard + " " - "but directories are not copied"); + "directory {} matched {} but directories are not copied", + name, wildcard); } } } @@ -375,7 +357,7 @@ void copy_glob_to_dir_if_better( std::string read_text_file(const context& cx, const fs::path& p, flags f) { - cx.trace(context::fs, "reading " + p.string()); + cx.trace(context::fs, "reading {}", p); std::string s; std::ifstream in(p); @@ -388,20 +370,13 @@ std::string read_text_file(const context& cx, const fs::path& p, flags f) if (in.bad()) { if (f & optional) - { - cx.debug(context::fs, - "can't read from " + p.string() + " (optional)"); - } + cx.debug(context::fs, "can't read from {} (optional)", p); else - { - cx.bail_out(context::fs, "can't read from " + p.string()); - } + cx.bail_out(context::fs, "can't read from {}", p); } else { - cx.trace(context::fs, - "finished reading " + p.string() + ", " + - std::to_string(s.size()) + " bytes"); + cx.trace(context::fs, "finished reading {}, {} bytes", p, s.size()); } return s; @@ -412,7 +387,7 @@ void write_text_file( { check(cx, p); - cx.trace(context::fs, "writing " + p.string()); + cx.trace(context::fs, "writing {} bytes to {}", s.size(), p); { std::ofstream out(p); @@ -423,19 +398,17 @@ void write_text_file( { if (f & optional) { - cx.debug(context::fs, - "can't write to " + p.string() + " (optional)"); + cx.debug(context::fs, "can't write to {} (optional)", p); } else { - cx.bail_out(context::fs, "can't write to " + p.string()); + cx.bail_out(context::fs, "can't write to {}", p); } } } cx.trace(context::fs, - "finished writing " + p.string() + ", " + - std::to_string(s.size()) + " bytes"); + "finished writing {} bytes to {}", s.size(), p); } @@ -445,7 +418,7 @@ void do_touch(const context& cx, const fs::path& p) std::ofstream out(p); if (!out) - cx.bail_out(context::fs, "failed to touch " + p.string()); + cx.bail_out(context::fs, "failed to touch {}", p); } void do_create_directories(const context& cx, const fs::path& p) @@ -454,7 +427,7 @@ void do_create_directories(const context& cx, const fs::path& p) fs::create_directories(p, ec); if (ec) - cx.bail_out(context::fs, "can't create " + p.string(), ec); + cx.bail_out(context::fs, "can't create {}, {}", p, ec.message()); } void do_delete_directory(const context& cx, const fs::path& p) @@ -468,8 +441,8 @@ void do_delete_directory(const context& cx, const fs::path& p) { cx.trace( context::fs, - "got access denied trying to delete dir " + p.string() + ", " - "trying to remove read-only flag recursively"); + "got access denied trying to delete dir {}, " + "trying to remove read-only flag recursively", p); remove_readonly(cx, p); fs::remove_all(p, ec); @@ -478,7 +451,7 @@ void do_delete_directory(const context& cx, const fs::path& p) return; } - cx.bail_out(context::fs, "failed to delete " + p.string(), ec); + cx.bail_out(context::fs, "failed to delete {}, {}", p, ec.message()); } } @@ -488,7 +461,7 @@ void do_delete_file(const context& cx, const fs::path& p) fs::remove(p, ec); if (ec) - cx.bail_out(context::fs, "can't delete " + p.string(), ec); + cx.bail_out(context::fs, "can't delete {}, {}", p, ec.message()); } void do_copy_file_to_dir( @@ -504,7 +477,7 @@ void do_copy_file_to_dir( if (ec) { cx.bail_out(context::fs, - "can't copy " + f.string() + " to " + d.string(), ec); + "can't copy {} to {}, {}", f, d, ec.message()); } } @@ -521,13 +494,13 @@ void do_copy_file_to_file( if (ec) { cx.bail_out(context::fs, - "can't copy " + src.string() + " to " + dest.string(), ec); + "can't copy {} to {}, {}", src, dest, ec.message()); } } void do_remove_readonly(const context& cx, const fs::path& p) { - cx.trace(context::fs, "chmod +x " + p.string()); + cx.trace(context::fs, "chmod +x {}", p); std::error_code ec; fs::permissions(p, fs::perms::owner_write, fs::perm_options::add, ec); @@ -535,7 +508,7 @@ void do_remove_readonly(const context& cx, const fs::path& p) if (ec) { cx.bail_out(context::fs, - "can't remove read-only flag on " + p.string(), ec); + "can't remove read-only flag on {}, {}", p, ec.message()); } } @@ -547,7 +520,7 @@ void do_rename(const context& cx, const fs::path& src, const fs::path& dest) if (ec) { cx.bail_out(context::fs, - "can't rename " + src.string() + " to " + dest.string(), ec); + "can't rename {} to {}, {}", src, dest, ec.message()); } } @@ -581,7 +554,7 @@ void check(const context& cx, const fs::path& p) if (is_inside(p, paths::licenses())) return; - cx.bail_out(context::fs, "path " + p.string() + " is outside prefix"); + cx.bail_out(context::fs, "path {} is outside prefix", p); } } // namespace diff --git a/src/pch.cpp b/src/pch.cpp index 1d9f38c..c290acb 100644 --- a/src/pch.cpp +++ b/src/pch.cpp @@ -1 +1,2 @@ #include "pch.h" +#include diff --git a/src/process.cpp b/src/process.cpp index c2a65cb..20a697f 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -40,7 +40,7 @@ handle_ptr async_pipe::create() if (ov_.hEvent == NULL) { const auto e = GetLastError(); - bail_out("CreateEvent failed", e); + bail_out("CreateEvent failed", error_message(e)); } event_.reset(ov_.hEvent); @@ -74,12 +74,12 @@ HANDLE async_pipe::create_pipe() HANDLE pipe_handle = ::CreateNamedPipeA( pipe_name.c_str(), PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, - 1, 50'000, 50'000, pipe_timeout, &sa); + 1, buffer_size, buffer_size, pipe_timeout, &sa); if (pipe_handle == INVALID_HANDLE_VALUE) { const auto e = GetLastError(); - bail_out("CreateNamedPipe failed", e); + bail_out("CreateNamedPipe failed", error_message(e)); } pipe.reset(pipe_handle); @@ -96,7 +96,7 @@ HANDLE async_pipe::create_pipe() if (!r) { const auto e = GetLastError(); - bail_out("DuplicateHandle for pipe", e); + bail_out("DuplicateHandle for pipe", error_message(e)); } stdout_.reset(output_read); @@ -111,7 +111,7 @@ HANDLE async_pipe::create_pipe() if (output_write == INVALID_HANDLE_VALUE) { const auto e = GetLastError(); - bail_out("CreateFileW for pipe failed", e); + bail_out("CreateFileW for pipe failed", error_message(e)); } return output_write; @@ -141,7 +141,7 @@ std::string_view async_pipe::try_read() default: { - bail_out("async_pipe read failed", e); + bail_out("async_pipe read failed", error_message(e)); break; } } @@ -149,6 +149,8 @@ std::string_view async_pipe::try_read() return {}; } + MOB_ASSERT(bytes_read <= buffer_size); + return {buffer_.get(), bytes_read}; } @@ -160,7 +162,7 @@ std::string_view async_pipe::check_pending() if (r == WAIT_FAILED) { const auto e = GetLastError(); - bail_out("WaitForSingleObject in async_pipe failed", e); + bail_out("WaitForSingleObject in async_pipe failed", error_message(e)); } if (!::GetOverlappedResult(stdout_.get(), &ov_, &bytes_read, FALSE)) @@ -187,7 +189,10 @@ std::string_view async_pipe::check_pending() default: { - bail_out("GetOverlappedResult failed in async_pipe", e); + bail_out( + "GetOverlappedResult failed in async_pipe", + error_message(e)); + break; } } @@ -195,6 +200,8 @@ std::string_view async_pipe::check_pending() return {}; } + MOB_ASSERT(bytes_read <= buffer_size); + ::ResetEvent(event_.get()); pending_ = false; @@ -214,13 +221,8 @@ process::impl& process::impl::operator=(const impl& i) } -process::process() : - cx_(&gcx()), flags_(process::noflags), - stdout_flags_(process::forward_to_log), - stdout_level_(context::level::trace), - stderr_flags_(process::forward_to_log), - stderr_level_(context::level::error), - code_(0) +process::process() + : cx_(&gcx()), flags_(process::noflags), code_(0) { } @@ -287,37 +289,37 @@ const fs::path& process::cwd() const process& process::stdout_flags(stream_flags s) { - stdout_flags_ = s; + stdout_.flags = s; return *this; } process& process::stdout_level(context::level lv) { - stdout_level_ = lv; + stdout_.level = lv; return *this; } process& process::stdout_filter(filter_fun f) { - stdout_filter_ = f; + stdout_.filter = f; return *this; } process& process::stderr_flags(stream_flags s) { - stderr_flags_ = s; + stderr_.flags = s; return *this; } process& process::stderr_level(context::level lv) { - stderr_level_ = lv; + stderr_.level = lv; return *this; } process& process::stderr_filter(filter_fun f) { - stderr_filter_ = f; + stderr_.filter = f; return *this; } @@ -368,10 +370,10 @@ void process::pipe_into(const process& p) void process::run() { if (!cwd_.empty()) - cx_->debug(context::cmd, "> cd " + cwd_.string()); + cx_->debug(context::cmd, "> cd {}", cwd_); const auto what = make_cmd(); - cx_->debug(context::cmd, "> " + what); + cx_->debug(context::cmd, "> {}", what); if (conf::dry()) return; @@ -387,8 +389,7 @@ void process::do_run(const std::string& what) if (fs::exists(error_log_file_)) { cx_->trace(context::cmd, - "external error log file " + - error_log_file_.string() + " exists, deleting"); + "external error log file {} exists, deleting", error_log_file_); op::delete_file(*cx_, error_log_file_, op::optional); } @@ -398,7 +399,7 @@ void process::do_run(const std::string& what) handle_ptr stdout_pipe, stderr_pipe; - switch (stdout_flags_) + switch (stdout_.flags) { case forward_to_log: case keep_in_string: @@ -421,7 +422,7 @@ void process::do_run(const std::string& what) } } - switch (stderr_flags_) + switch (stderr_.flags) { case forward_to_log: case keep_in_string: @@ -470,10 +471,11 @@ void process::do_run(const std::string& what) if (!r) { const auto e = GetLastError(); - cx_->bail_out(context::cmd, "failed to start '" + cmd + "'", e); + cx_->bail_out(context::cmd, + "failed to start '{}', {}", cmd, error_message(e)); } - cx_->trace(context::cmd, "pid " + std::to_string(pi.dwProcessId)); + cx_->trace(context::cmd, "pid {}", pi.dwProcessId); ::CloseHandle(pi.hThread); impl_.handle.reset(pi.hProcess); @@ -512,7 +514,8 @@ void process::join() else { const auto e = GetLastError(); - cx_->bail_out(context::cmd, "failed to wait on process", e); + cx_->bail_out(context::cmd, + "failed to wait on process", error_message(e)); } } @@ -524,27 +527,39 @@ bool process::read_pipes() { bool read_something = false; - // stdout - switch (stdout_flags_) + if (read_pipe(stdout_, impl_.stdout_pipe, context::std_out)) + read_something = true; + + if (read_pipe(stderr_, impl_.stderr_pipe, context::std_err)) + read_something = true; + + return read_something; +} + +bool process::read_pipe(stream& s, async_pipe& pipe, context::reason r) +{ + bool read_something = false; + + switch (s.flags) { case forward_to_log: { - std::string_view s = impl_.stdout_pipe.read(); - if (!s.empty()) + const std::string_view buffer = pipe.read(); + if (!buffer.empty()) read_something = true; - for_each_line(s, [&](auto&& line) + for_each_line(buffer, [&](auto&& line) { - filter f = {line, context::std_out, stdout_level_, false}; + filter f = {line, r, s.level, false}; - if (stdout_filter_) + if (s.filter) { - stdout_filter_(f); + s.filter(f); if (f.ignore) return; } - cx_->log(f.r, f.lv, f.line); + cx_->log(f.r, f.lv, "{}", f.line); }); break; @@ -552,52 +567,11 @@ bool process::read_pipes() case keep_in_string: { - std::string_view s = impl_.stdout_pipe.read(); - if (!s.empty()) + const std::string_view buffer = pipe.read(); + if (!buffer.empty()) read_something = true; - stdout_string_ += s; - break; - } - - case bit_bucket: - case inherit: - break; - } - - - switch (stderr_flags_) - { - case forward_to_log: - { - std::string_view s = impl_.stderr_pipe.read(); - if (!s.empty()) - read_something = true; - - for_each_line(s, [&](auto&& line) - { - filter f = {line, context::std_err, stderr_level_, false}; - - if (stderr_filter_) - { - stderr_filter_(f); - if (f.ignore) - return; - } - - cx_->log(f.r, f.lv, f.line); - }); - - break; - } - - case keep_in_string: - { - std::string_view s = impl_.stderr_pipe.read(); - if (!s.empty()) - read_something = true; - - stderr_string_ += s; + s.string.append(buffer.begin(), buffer.end()); break; } @@ -624,7 +598,10 @@ void process::on_completed() if (!GetExitCodeProcess(impl_.handle.get(), &code_)) { const auto e = GetLastError(); - cx_->error(context::cmd, "failed to get exit code", e); + + cx_->error(context::cmd, + "failed to get exit code, ", error_message(e)); + code_ = 0xffff; } @@ -643,9 +620,7 @@ void process::on_completed() else { dump_error_log_file(); - - cx_->bail_out(context::cmd, - make_name() + " returned " + std::to_string(code_)); + cx_->bail_out(context::cmd, "{} returned {}", make_name(), code_); } } @@ -675,9 +650,7 @@ void process::on_timeout(bool& already_interrupted) } else { - cx_->trace(context::cmd, - "sending sigint to " + std::to_string(pid)); - + cx_->trace(context::cmd, "sending sigint to {}", pid); GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid); } } @@ -702,19 +675,17 @@ void process::dump_error_log_file() noexcept return; cx_->error(context::cmd, - make_name() + " failed, " - "content of " + error_log_file_.string() + ":"); + "{} failed, content of {}:", make_name(), error_log_file_); for_each_line(log, [&](auto&& line) { - cx_->error(context::cmd, std::string(8, ' ') + std::string(line)); + cx_->error(context::cmd, " {}", line); }); } else { cx_->debug(context::cmd, - "external error log file " + error_log_file_.string() + " " - "doesn't exist"); + "external error log file {} doesn't exist", error_log_file_); } } catch(...) @@ -728,14 +699,14 @@ int process::exit_code() const return static_cast(code_); } -std::string process::steal_stdout() +std::string process::stdout_string() { - return std::move(stdout_string_); + return stdout_.string; } -std::string process::steal_stderr() +std::string process::stderr_string() { - return std::move(stderr_string_); + return stderr_.string; } void process::add_arg(const std::string& k, const std::string& v, arg_flags f) diff --git a/src/process.h b/src/process.h index 22a4a33..745b87b 100644 --- a/src/process.h +++ b/src/process.h @@ -19,7 +19,7 @@ public: std::string_view read(); private: - static const std::size_t buffer_size = 50000; + static const std::size_t buffer_size = 50'000; handle_ptr stdout_; handle_ptr event_; @@ -155,8 +155,8 @@ public: void join(); int exit_code() const; - std::string steal_stdout(); - std::string steal_stderr(); + std::string stdout_string(); + std::string stderr_string(); private: struct impl @@ -170,22 +170,21 @@ private: impl& operator=(const impl&); }; + struct stream + { + stream_flags flags = forward_to_log; + context::level level = context::level::trace; + filter_fun filter; + std::string string; + }; + const context* cx_; std::string name_; fs::path bin_; fs::path cwd_; flags_t flags_; - - stream_flags stdout_flags_; - context::level stdout_level_; - filter_fun stdout_filter_; - std::string stdout_string_; - - stream_flags stderr_flags_; - context::level stderr_level_; - filter_fun stderr_filter_; - std::string stderr_string_; - + stream stdout_; + stream stderr_; mob::env env_; std::string raw_; std::string cmd_; @@ -200,6 +199,7 @@ private: void do_run(const std::string& what); bool read_pipes(); + bool read_pipe(stream& s, async_pipe& pipe, context::reason r); void on_completed(); void on_timeout(bool& already_interrupted); diff --git a/src/tasks/boost.cpp b/src/tasks/boost.cpp index 85a3a80..6422d1b 100644 --- a/src/tasks/boost.cpp +++ b/src/tasks/boost.cpp @@ -115,7 +115,7 @@ void boost::build_and_install_from_source() if (fs::exists(b2_exe())) { cx().trace(context::bypass, - b2_exe().string() + " exists, boost already bootstrapped"); + "{} exists, boost already bootstrapped", b2_exe()); } else { @@ -176,11 +176,11 @@ void boost::write_config_jam() << " ;"; cx().trace(context::generic, - "writing config file at " + config_jam_file().string() + ":"); + "writing config file at {}:", config_jam_file()); for_each_line(oss.str(), [&](auto&& line) { - cx().trace(context::generic, std::string(8, ' ') + std::string(line)); + cx().trace(context::generic, " {}", line); }); @@ -196,7 +196,7 @@ std::smatch boost::parse_boost_version() std::smatch m; if (!std::regex_match(version(), m, re)) - bail_out("bad boost version '" + version() + "'"); + bail_out("bad boost version '{}'", version()); return m; } diff --git a/src/tasks/modorganizer.cpp b/src/tasks/modorganizer.cpp index 8678fef..830d593 100644 --- a/src/tasks/modorganizer.cpp +++ b/src/tasks/modorganizer.cpp @@ -87,7 +87,7 @@ void modorganizer::do_build_and_install() if (!fs::exists(this_source_path() / "CMakeLists.txt")) { cx().trace(context::generic, - repo_ + " has no CMakeLists.txt, not building"); + "{} has no CMakeLists.txt, not building", repo_); return; } diff --git a/src/tasks/nmm.cpp b/src/tasks/nmm.cpp index 4494791..aa4ecc6 100644 --- a/src/tasks/nmm.cpp +++ b/src/tasks/nmm.cpp @@ -60,9 +60,9 @@ void nmm::do_build_and_install() } cx().debug(context::generic, - "msbuild multiprocess has failed more than " + - std::to_string(max_tries) + " times for nmm, restarting one last " - "time single process; that one should work"); + "msbuild multiprocess has failed more than {} times for nmm, " + "restarting one last time single process; that one should work", + max_tries); run_tool(msbuild() .solution(source_path() / "NexusClient.sln") diff --git a/src/tasks/openssl.cpp b/src/tasks/openssl.cpp index 20e4d78..c6eea9d 100644 --- a/src/tasks/openssl.cpp +++ b/src/tasks/openssl.cpp @@ -137,8 +137,9 @@ void openssl::install_engines() } cx().debug(context::generic, - "jom /J has failed more than " + std::to_string(max_tries) + " " - "times, restarting one last time without /J; that one should work"); + "jom /J has failed more than {} times, " + "restarting one last time without /J; that one should work", + max_tries); run_tool(jom() .path(source_path()) @@ -205,7 +206,7 @@ std::smatch openssl::parse_version() std::smatch m; if (!std::regex_match(version(), m, re)) - bail_out("bad openssl version '" + version() + "'"); + bail_out("bad openssl version '{}'", version()); return m; } diff --git a/src/tasks/python.cpp b/src/tasks/python.cpp index 89d5405..7a8c81c 100644 --- a/src/tasks/python.cpp +++ b/src/tasks/python.cpp @@ -27,7 +27,7 @@ python::version_info python::parsed_version() std::smatch m; if (!std::regex_match(version(), m, re)) - bail_out("bad python version '" + version() + "'"); + bail_out("bad python version '{}'", version()); version_info v; diff --git a/src/tasks/sevenz.cpp b/src/tasks/sevenz.cpp index 359b3a6..ca87718 100644 --- a/src/tasks/sevenz.cpp +++ b/src/tasks/sevenz.cpp @@ -65,8 +65,9 @@ void sevenz::build() } cx().debug(context::generic, - "jom /J has failed more than " + std::to_string(max_tries) + " " - "times, restarting one last time without /J; that one should work"); + "jom /J has failed more than {} times, " + "restarting one last time without /J; that one should work", + max_tries); run_tool(jom() .path(module_to_build()) diff --git a/src/tasks/sip.cpp b/src/tasks/sip.cpp index 6347962..c1e7f6c 100644 --- a/src/tasks/sip.cpp +++ b/src/tasks/sip.cpp @@ -47,7 +47,7 @@ fs::path sip::module_source_path() std::smatch m; if (!std::regex_match(version_for_pyqt(), m, re)) - bail_out("bad pyqt sip version " + version_for_pyqt()); + bail_out("bad pyqt sip version {}", version_for_pyqt()); // 12.7 const auto dir = m[1].str() + "." + m[2].str(); @@ -86,15 +86,13 @@ void sip::download() { if (conf::redownload()) { - cx().trace(context::redownload, - "deleting " + download_file().string()); - + cx().trace(context::redownload, "deleting {}", download_file()); op::delete_file(cx(), download_file(), op::optional); } else { cx().trace(context::bypass, - "sip: " + download_file().string() + " already exists"); + "sip: {} already exists", download_file()); return; } @@ -118,11 +116,11 @@ void sip::generate() { if (conf::rebuild()) { - cx().trace(context::rebuild, "ignoring " + header.string()); + cx().trace(context::rebuild, "ignoring {}", header); } else { - cx().trace(context::bypass, header.string() + " already exists"); + cx().trace(context::bypass, "{} already exists", header); return; } } diff --git a/src/tasks/task.cpp b/src/tasks/task.cpp index 7d83e0e..ad8fe4f 100644 --- a/src/tasks/task.cpp +++ b/src/tasks/task.cpp @@ -51,8 +51,7 @@ void run_tasks(const std::vector tasks) { { std::set set(tasks.begin(), tasks.end()); - if (set.size() != tasks.size()) - DebugBreak(); + MOB_ASSERT(set.size() == tasks.size()); } for (auto* t : tasks) @@ -92,9 +91,9 @@ void run_tasks(const std::vector& names) return; if (names.size() == 1) - gcx().debug(context::generic, "specified task: " + names[0]); + gcx().debug(context::generic, "specified task: {}", names[0]); else - gcx().debug(context::generic, "specified tasks: " + join(names, " ")); + gcx().debug(context::generic, "specified tasks: {}", join(names, " ")); std::vector tasks; std::set seen; @@ -224,7 +223,7 @@ void task::threaded_run(std::string thread_name, std::function f) } catch(bailed e) { - error(name() + " bailed out, interrupting all tasks"); + error("{} bailed out, interrupting all tasks", name()); interrupt_all(); } catch(interrupted) @@ -233,7 +232,7 @@ void task::threaded_run(std::string thread_name, std::function f) } catch(std::exception& e) { - error(name() + " uncaught exception: " + e.what()); + error("{} uncaught exception: {}", name(), e.what()); interrupt_all(); } } @@ -349,7 +348,7 @@ void task::run_tool_impl(tool* t) } }); - cx().debug(context::generic, "running tool " + t->name()); + cx().debug(context::generic, "running tool {}", t->name()); context cxcopy(cx()); diff --git a/src/tasks/task.h b/src/tasks/task.h index fad3fd4..7c50406 100644 --- a/src/tasks/task.h +++ b/src/tasks/task.h @@ -85,7 +85,7 @@ protected: for (auto&& [name, f] : v) { - cx().trace(context::generic, "running in parallel: " + name); + cx().trace(context::generic, "running in parallel: {}", name); ts.push_back(std::thread([this, name, f] { diff --git a/src/tools/downloader.cpp b/src/tools/downloader.cpp index 6075b0c..e79092c 100644 --- a/src/tools/downloader.cpp +++ b/src/tools/downloader.cpp @@ -60,7 +60,7 @@ void downloader::do_run() cx_->trace(context::net, "no cached downloads were found, will try:"); for (auto&& u : urls_) - cx_->trace(context::net, " . " + u.string()); + cx_->trace(context::net, " . {}", u); // try them in order @@ -69,8 +69,7 @@ void downloader::do_run() if (file_.empty()) file_ = path_for_url(u); - cx_->trace(context::net, - "trying " + u.string() + " into " + file_.string()); + cx_->trace(context::net, "trying {} into {}", u, file_); dl_->start(u, file_); cx_->trace(context::net, "waiting for download"); @@ -78,7 +77,7 @@ void downloader::do_run() if (dl_->ok()) { - cx_->trace(context::net, "file " + file_.string() + " downloaded"); + cx_->trace(context::net, "file {} downloaded", file_); return; } @@ -107,18 +106,18 @@ bool downloader::try_picking(const fs::path& file) { if (conf::redownload()) { - cx_->trace(context::redownload, "deleting " + file.string()); + cx_->trace(context::redownload, "deleting {}", file); op::delete_file(*cx_, file, op::optional); } else { - cx_->trace(context::bypass, "picking " + file_.string()); + cx_->trace(context::bypass, "picking {}", file_); return true; } } else { - cx_->trace(context::net, "no " + file.string()); + cx_->trace(context::net, "no {}", file); } return false; @@ -136,13 +135,12 @@ fs::path downloader::path_for_url(const mob::url& u) const const std::string strip = "/download"; cx_->trace(context::net, - "url " + u.string() + " is sourceforge, " - "stripping " + strip + " for filename"); + "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.string()); + 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 8aefc3d..a874e0e 100644 --- a/src/tools/extractor.cpp +++ b/src/tools/extractor.cpp @@ -34,21 +34,17 @@ void extractor::do_run() { if (conf::reextract()) { - cx_->debug(context::reextract, "deleting " + where_.string()); + cx_->debug(context::reextract, "deleting {}", where_); op::delete_directory(*cx_, where_, op::optional); } else { - cx_->debug( - context::bypass, - "directory " + where_.string() + " already exists"); - + cx_->debug(context::bypass, "directory {} already exists", where_); return; } } - cx_->debug(context::generic, - "extracting " + file_.string() + " into " + where_.string()); + cx_->debug(context::generic, "extracting {} into {}", file_, where_); ifile.create(); @@ -119,14 +115,15 @@ void extractor::check_duplicate_directory(const fs::path& ifile) if (!fs::exists(where_ / dir_name)) { cx_->trace(context::generic, - "no duplicate subdir " + dir_name + ", leaving as-is"); + "no duplicate subdir {}, leaving as-is", dir_name); return; } cx_->trace(context::generic, - "found subdir " + dir_name + " with same name as output dir; " - "moving everything up one"); + "found subdir {} with same name as output dir; " + "moving everything up one", + dir_name); // the archive contained a directory with the same name as the output // directory @@ -148,12 +145,12 @@ 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, - "check_duplicate_directory: " + e.path().string() + " is " - "yet another directory"); + "check_duplicate_directory: {} is yet another directory", + e.path()); } cx_->trace(context::generic, - "assuming file " + e.path().string() + " is useless, deleting"); + "assuming file {} is useless, deleting", e.path()); op::delete_file(*cx_, e.path()); } @@ -166,13 +163,12 @@ void extractor::check_duplicate_directory(const fs::path& ifile) const auto temp_dir = where_ / ("_mob_" + dir_name ); cx_->trace(context::generic, - "renaming dir to " + temp_dir.string() + " to avoid clashes"); + "renaming dir to {} to avoid clashes", temp_dir); if (fs::exists(temp_dir)) { cx_->trace(context::generic, - "temp dir " + temp_dir.string() + " already exists, " - "deleting"); + "temp dir {} already exists, deleting", temp_dir); op::delete_directory(*cx_, temp_dir); } diff --git a/src/tools/patcher.cpp b/src/tools/patcher.cpp index a72fff2..a40d55b 100644 --- a/src/tools/patcher.cpp +++ b/src/tools/patcher.cpp @@ -33,23 +33,21 @@ void patcher::do_run() if (!fs::exists(patches_)) { cx_->trace(context::generic, - "patch directory " + patches_.string() + " doesn't exist, " - "assuming no patches"); + "patch directory {} doesn't exist, assuming no patches", patches_); return; } if (file_.empty()) { - cx_->trace(context::generic, - "looking for patches in " + patches_.string()); + cx_->trace(context::generic, "looking for patches in {}", patches_); for (auto e : fs::directory_iterator(patches_)) { if (!e.is_regular_file()) { cx_->trace(context::generic, - "skipping " + e.path().string() + ", not a file"); + "skipping {}, not a file", e.path()); continue; } @@ -59,14 +57,14 @@ void patcher::do_run() if (p.extension() == ".manual_patch") { cx_->trace(context::generic, - "skipping manual patch " + e.path().string()); + "skipping manual patch {}", e.path()); continue; } else if (p.extension() != ".patch") { cx_->warning(context::generic, - "file with unknown extension " + p.string()); + "file with unknown extension {}", p); continue; } @@ -76,9 +74,7 @@ void patcher::do_run() } else { - cx_->trace(context::generic, - "doing manual patch from " + file_.string()); - + cx_->trace(context::generic, "doing manual patch from {}", file_); do_patch(patches_ / file_); } } @@ -104,8 +100,7 @@ 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.string()); + cx_->trace(context::generic, "trying to patch using {}", patch_file); { // check @@ -119,7 +114,7 @@ void patcher::do_patch(const fs::path& patch_file) if (ret == 0) { cx_->trace(context::generic, - "patch " + patch_file.string() + " already applied"); + "patch {} already applied", patch_file); return; } @@ -130,15 +125,14 @@ void patcher::do_patch(const fs::path& patch_file) } else { - cx_->bail_out(context::generic, - "patch returned " + std::to_string(ret)); + cx_->bail_out(context::generic, "patch returned {}", ret); } } { // apply - cx_->trace(context::generic, "applying patch " + patch_file.string()); + cx_->trace(context::generic, "applying patch {}", patch_file); process_ = apply; execute_and_join(); } diff --git a/src/tools/tools.cpp b/src/tools/tools.cpp index 73d5e4f..30e3117 100644 --- a/src/tools/tools.cpp +++ b/src/tools/tools.cpp @@ -48,7 +48,7 @@ void tool::interrupt() { if (!interrupted_) { - cx_->debug(context::interruption, "interrupting " + name_); + cx_->debug(context::interruption, "interrupting {}", name_); interrupted_ = true; do_interrupt(); } diff --git a/src/utility.cpp b/src/utility.cpp index aeb52bd..1f9a4ba 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -13,6 +13,33 @@ extern u8stream u8cout(std::wcout); extern u8stream u8cerr(std::wcerr); +void u8stream::do_output(const std::string& s) +{ + out_ << utf8_to_utf16(s); +} + + +void mob_assertion_failed( + const char* message, + const char* exp, const char* file, int line, const char* func) +{ + if (message) + { + gcx().error(context::generic, + "assertion failed: {}:{} {}: {} ({})", + file, line, func, message, exp); + } + else + { + gcx().error(context::generic, + "assertion failed: {}:{} {}: '{}'", + file, line, func, exp); + } + + if (IsDebuggerPresent()) + DebugBreak(); +} + url make_github_url(const std::string& org, const std::string& repo) { return "https://github.com/" + org + "/" + repo + ".git"; @@ -120,7 +147,7 @@ std::string pad_left(std::string s, std::size_t n, char c) file_deleter::file_deleter(const context& cx, fs::path p) : cx_(cx), p_(std::move(p)), delete_(true) { - cx_.trace(context::fs, "will delete " + p_.string() + " if things go bad"); + cx_.trace(context::fs, "will delete {} if things go bad", p_); } file_deleter::~file_deleter() @@ -138,13 +165,13 @@ file_deleter::~file_deleter() void file_deleter::delete_now() { - cx_.debug(context::fs, "something went bad, deleting " + p_.string()); + cx_.debug(context::fs, "something went bad, deleting {}", p_); op::delete_file(cx_, p_, op::optional); } void file_deleter::cancel() { - cx_.trace(context::fs, "everything okay, keeping " + p_.string()); + cx_.trace(context::fs, "everything okay, keeping {}", p_); delete_ = false; } @@ -152,7 +179,7 @@ void file_deleter::cancel() directory_deleter::directory_deleter(const context& cx, fs::path p) : cx_(cx), p_(std::move(p)), delete_(true) { - cx_.trace(context::fs, "will delete " + p_.string() + " if things go bad"); + cx_.trace(context::fs, "will delete {} if things go bad", p_); } directory_deleter::~directory_deleter() @@ -170,13 +197,13 @@ directory_deleter::~directory_deleter() void directory_deleter::delete_now() { - cx_.debug(context::fs, "something went bad, deleting " + p_.string()); + cx_.debug(context::fs, "something went bad, deleting {}", p_); op::delete_directory(cx_, p_, op::optional); } void directory_deleter::cancel() { - cx_.trace(context::fs, "everything okay, keeping " + p_.string()); + cx_.trace(context::fs, "everything okay, keeping {}", p_); delete_ = false; } @@ -186,10 +213,7 @@ interruption_file::interruption_file( : cx_(cx), dir_(std::move(dir)), name_(std::move(name)) { if (fs::exists(file())) - { - cx_.trace(context::interruption, - "found interrupt file " + file().string()); - } + cx_.trace(context::interruption, "found interrupt file {}", file()); } bool interruption_file::exists() const @@ -204,17 +228,13 @@ fs::path interruption_file::file() const void interruption_file::create() { - cx_.trace(context::interruption, - "creating interrupt file " + file().string()); - + cx_.trace(context::interruption, "creating interrupt file {}", file()); op::touch(cx_, file()); } void interruption_file::remove() { - cx_.trace(context::interruption, - "removing interrupt file " + file().string()); - + cx_.trace(context::interruption, "removing interrupt file {}", file()); op::delete_file(cx_, file()); } @@ -230,8 +250,8 @@ bool bypass_file::exists() const { if (conf::rebuild()) { - cx_.trace(context::bypass, - "bypass file " + file_.string() + " exists, deleting"); + cx_.trace(context::rebuild, + "bypass file {} exists, deleting", file_); op::delete_file(cx_, file_, op::optional); @@ -239,26 +259,20 @@ bool bypass_file::exists() const } else { - cx_.trace(context::bypass, - "bypass file " + file_.string() + " exists"); - + cx_.trace(context::bypass, "bypass file {} exists", file_); return true; } } else { - cx_.trace(context::bypass, - "bypass file " + file_.string() + " not found"); - + cx_.trace(context::bypass, "bypass file {} not found", file_); return false; } } void bypass_file::create() { - cx_.trace(context::bypass, - "create bypass file " + file_.string()); - + cx_.trace(context::bypass, "create bypass file {}", file_); op::touch(cx_, file_); } @@ -383,10 +397,12 @@ std::optional to_utf16(UINT from, std::string_view s) from, 0, s.data(), static_cast(s.size()), buffer.get(), wsize); - if (wsize == 0) + if (written == 0) return {}; - return std::wstring(buffer.get(), buffer.get() + wsize); + MOB_ASSERT(written == wsize); + + return std::wstring(buffer.get(), buffer.get() + written); } std::optional to_utf8(std::wstring_view ws) @@ -408,10 +424,12 @@ std::optional to_utf8(std::wstring_view ws) CP_UTF8, 0, ws.data(), static_cast(ws.size()), buffer.get(), size, nullptr, nullptr); - if (size == 0) + if (written == 0) return {}; - return std::string(buffer.get(), buffer.get() + size); + MOB_ASSERT(written == size); + + return std::string(buffer.get(), buffer.get() + written); } @@ -470,16 +488,4 @@ std::string path_to_utf8(const fs::path& p) return utf16_to_utf8(p.native()); } - -void output_to_stdout(std::string_view utf8) -{ - std::wcout << utf8_to_utf16(utf8); -} - -void output_to_stderr(std::string_view utf8) -{ - std::wcerr << utf8_to_utf16(utf8); -} - } // namespace - diff --git a/src/utility.h b/src/utility.h index 2555aca..fcb63e0 100644 --- a/src/utility.h +++ b/src/utility.h @@ -7,6 +7,30 @@ namespace mob inline E operator|(E e1, E e2) { return (E)((int)e1 | (int)e2); } \ inline E operator|=(E& e1, E e2) { e1 = e1 | e2; return e1; } +#define MOB_ASSERT(x, ...) \ + mob_assert(x, __VA_ARGS__, #x, __FILE__, __LINE__, __FUNCSIG__); + +void mob_assertion_failed( + const char* message, + const char* exp, const char* file, int line, const char* func); + +template +inline void mob_assert( + X&& x, const char* message, + const char* exp, const char* file, int line, const char* func) +{ + if (!(x)) + mob_assertion_failed(message, exp, file, line, func); +} + +template +inline void mob_assert( + X&& x, const char* exp, const char* file, int line, const char* func) +{ + if (!(x)) + mob_assertion_failed(nullptr, exp, file, line, func); +} + class context; class url; @@ -207,13 +231,15 @@ public: std::ostringstream oss; ((oss << std::forward(args)), ...); - out_ << utf8_to_utf16(oss.str()); + do_output(oss.str()); return *this; } private: std::wostream& out_; + + void do_output(const std::string& s); }; @@ -221,27 +247,41 @@ extern u8stream u8cout; extern u8stream u8cerr; -void output_to_stdout(std::string_view utf8); -void output_to_stderr(std::string_view utf8); - template void for_each_line(std::string_view s, F&& f) { - const char* start = s.data(); - const char* end = s.data() + s.size(); - const char* p = start; + if (s.empty()) + return; + + const char* const begin = s.data(); + const char* const end = s.data() + s.size(); + + const char* start = begin; + const char* p = begin; for (;;) { + MOB_ASSERT(p && p >= begin && p <= end); + MOB_ASSERT(start && start >= begin && start <= end); + if (p == end || *p == '\n' || *p == '\r') { if (p != start) - f(std::string_view(start, static_cast(p - start))); + { + MOB_ASSERT(p >= start); + + const auto n = static_cast(p - start); + MOB_ASSERT(n <= s.size()); + + f(std::string_view(start, n)); + } while (p != end && (*p == '\n' || *p == '\r')) ++p; + MOB_ASSERT(p && p >= begin && p <= end); + if (p == end) break;