diff --git a/src/cmd/build.cpp b/src/cmd/build.cpp index bcda3f1..2ce10b6 100644 --- a/src/cmd/build.cpp +++ b/src/cmd/build.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "commands.h" +#include "../core/ini.h" #include "../tasks/tasks.h" namespace mob diff --git a/src/cmd/commands.cpp b/src/cmd/commands.cpp index 7bcfeb1..594fa59 100644 --- a/src/cmd/commands.cpp +++ b/src/cmd/commands.cpp @@ -3,6 +3,7 @@ #include "../utility.h" #include "../net.h" #include "../core/conf.h" +#include "../core/ini.h" #include "../tasks/tasks.h" #include "../tools/tools.h" #include "../utility/threading.h" diff --git a/src/core/conf.cpp b/src/core/conf.cpp index b4d4859..8ef295e 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -2,6 +2,7 @@ #include "conf.h" #include "context.h" #include "env.h" +#include "ini.h" #include "../utility.h" #include "../tasks/task.h" #include "../tools/tools.h" @@ -177,52 +178,6 @@ void set_string_for_task( namespace mob { -std::string default_ini_filename() -{ - return "mob.ini"; -} - - -conf_global conf::global() -{ - return {}; -} - -conf_task conf::task(const std::vector& names) -{ - return {names}; -} - -conf_tools conf::tool() -{ - return {}; -} - -conf_transifex conf::transifex() -{ - return {}; -} - -conf_prebuilt conf::prebuilt() -{ - return {}; -} - -conf_versions conf::version() -{ - return {}; -} - -conf_paths conf::path() -{ - return {}; -} - -bool conf::dry() -{ - return conf().global().dry(); -} - std::vector format_options() { std::size_t longest_task = 0; @@ -273,31 +228,103 @@ std::vector format_options() return lines; } - -struct parsed_option +fs::path find_iscc() { - std::string task, section, key, value; -}; + const auto tasks = find_tasks("installer"); + MOB_ASSERT(tasks.size() == 1); -parsed_option parse_option(const std::string& s) -{ - // task:section/key=value - // task: is optional - std::regex re(R"((?:(.+)\:)?(.+)/(.*)=(.*))"); - std::smatch m; + if (!tasks[0]->enabled()) + return {}; - if (!std::regex_match(s, m, re)) + auto iscc = conf().tool().get("iscc"); + if (iscc.is_absolute()) { - gcx().bail_out(context::conf, - "bad option {}, must be [task:]section/key=value", s); + if (!fs::exists(iscc)) + { + gcx().bail_out(context::conf, + "{} doesn't exist (from ini, absolute path)", iscc); + } + + return iscc; } - std::string task = trim_copy(m[1].str()); - std::string section = trim_copy(m[2].str()); - std::string key = trim_copy(m[3].str()); - std::string value = trim_copy(m[4].str()); + fs::path p = find_in_path(path_to_utf8(iscc)); + if (fs::exists(p)) + return fs::canonical(fs::absolute(p)); - return {task, section, key, value}; + for (int v : {5, 6, 7, 8}) + { + const fs::path inno = fmt::format("inno setup {}", v); + + for (fs::path pf : {conf().path().pf_x86(), conf().path().pf_x64()}) + { + p = pf / inno / iscc; + if (fs::exists(p)) + return fs::canonical(fs::absolute(p)); + } + } + + gcx().bail_out(context::conf, "can't find {} anywhere", iscc); +} + +void set_special_options() +{ + conf().global().set_output_log_level( + details::get_string("global", "output_log_level")); + + conf().global().set_file_log_level( + details::get_string("global", "file_log_level")); + + conf().global().set_dry( + details::get_string("global", "dry")); +} + +template +void set_path_if_empty(std::string_view k, F&& f) +{ + fs::path p = details::get_string("paths", k); + + if (p.empty()) + { + if constexpr (std::is_same_v>) + p = f; + else + p = f(); + } + + p = fs::absolute(p); + + if (!conf::dry()) + { + if (!fs::exists(p)) + gcx().bail_out(context::conf, "path {} not found", p); + + p = fs::canonical(p); + } + + details::set_string("paths", k, path_to_utf8(p)); +} + +void make_canonical_path( + std::string_view key, + const fs::path& default_parent, std::string_view default_dir) +{ + fs::path p = conf().path().get(key); + + if (p.empty()) + { + p = default_parent / default_dir; + } + else + { + if (p.is_relative()) + p = default_parent / p; + } + + if (!conf::dry()) + p = fs::weakly_canonical(fs::absolute(p)); + + details::set_string("paths", key, path_to_utf8(p)); } bool try_parts(fs::path& check, const std::vector& parts) @@ -352,7 +379,7 @@ fs::path mob_exe_path() gcx().bail_out(context::conf, "can't get module filename"); } -fs::path find_root(bool verbose=false) +fs::path find_root(bool verbose) { gcx().trace(context::conf, "looking for root directory"); @@ -371,7 +398,7 @@ fs::path find_root(bool verbose=false) { p = p.parent_path(); if (p.filename().u8string() == u8"Debug" || - p.filename().u8string() == u8"Release") + p.filename().u8string() == u8"Release") { if (verbose) u8cout << "this is mob's build directory, looking up\n"; @@ -664,384 +691,6 @@ void find_vcvars() } -template -void ini_error( - const fs::path& ini, std::size_t line, std::string_view f, Args&&... args) -{ - gcx().bail_out(context::conf, - "{}:{}: {}", - path_to_utf8(ini), (line + 1), - fmt::format(f, std::forward(args)...)); -} - -std::vector read_ini(const fs::path& ini) -{ - std::ifstream in(ini); - - std::vector lines; - - for (;;) - { - std::string line; - std::getline(in, line); - trim(line); - - if (!in) - break; - - lines.push_back(std::move(line)); - } - - if (in.bad()) - gcx().bail_out(context::conf, "failed to read ini {}", ini); - - return lines; -} - -void parse_section( - const fs::path& ini, std::size_t& i, - const std::vector& lines, - std::string_view task, std::string_view section, bool add) -{ - ++i; - - for (;;) - { - if (i >= lines.size() || lines[i][0] == '[') - break; - - const auto& line = lines[i]; - - if (line.empty() || line[0] == '#' || line[0] == ';') - { - ++i; - continue; - } - - const auto sep = line.find("="); - if (sep == std::string::npos) - ini_error(ini, i, "bad line '{}'", line); - - const std::string k = trim_copy(line.substr(0, sep)); - const std::string v = trim_copy(line.substr(sep + 1)); - - if (k.empty()) - ini_error(ini, i, "bad line '{}'", line); - - if (section == "aliases") - { - add_alias(k, split_quoted(v, " ")); - } - else if (task.empty()) - { - if (add) - details::add_string(section, k, v); - else - details::set_string(section, k, v); - } - else - { - if (task == "_override") - { - details::set_string_for_task("_override", section, k, v); - } - else - { - const auto& tasks = find_tasks(task); - - if (tasks.empty()) - ini_error(ini, i, "no task matching '{}' found", task); - - for (auto& t : tasks) - details::set_string_for_task(t->name(), section, k, v); - } - } - - ++i; - } -} - -void parse_ini(const fs::path& ini, bool add) -{ - gcx().debug(context::conf, "using ini at {}", ini); - - const auto lines = read_ini(ini); - std::size_t i = 0; - - for (;;) - { - if (i >= lines.size()) - break; - - const auto& line = lines[i]; - if (line.empty() || line[0] == '#' || line[0] == ';') - { - ++i; - continue; - } - - if (line.starts_with("[") && line.ends_with("]")) - { - const std::string s = line.substr(1, line.size() - 2); - - std::string task, section; - - const auto col = s.find(":"); - - if (col == std::string::npos) - { - section = s; - } - else - { - task = s.substr(0, col); - section = s.substr(col + 1); - } - - parse_section(ini, i, lines, task, section, add); - } - else - { - ini_error(ini, i, "bad line '{}'", line); - } - } -} - - -template -void set_path_if_empty(std::string_view k, F&& f) -{ - fs::path p = details::get_string("paths", k); - - if (p.empty()) - { - if constexpr (std::is_same_v>) - p = f; - else - p = f(); - } - - p = fs::absolute(p); - - if (!conf::dry()) - { - if (!fs::exists(p)) - gcx().bail_out(context::conf, "path {} not found", p); - - p = fs::canonical(p); - } - - details::set_string("paths", k, path_to_utf8(p)); -} - -void make_canonical_path( - std::string_view key, - const fs::path& default_parent, std::string_view default_dir) -{ - fs::path p = conf().path().get(key); - - if (p.empty()) - { - p = default_parent / default_dir; - } - else - { - if (p.is_relative()) - p = default_parent / p; - } - - if (!conf::dry()) - p = fs::weakly_canonical(fs::absolute(p)); - - details::set_string("paths", key, path_to_utf8(p)); -} - -void set_special_options() -{ - conf().global().set_output_log_level( - details::get_string("global", "output_log_level")); - - conf().global().set_file_log_level( - details::get_string("global", "file_log_level")); - - conf().global().set_dry( - details::get_string("global", "dry")); -} - -std::vector find_inis( - bool auto_detect, const std::vector& from_cl, bool verbose) -{ - // the string is just for verbose - std::vector> v; - - // adds a path to the vector; if the path already exists, moves it to - // the last element - auto add_or_move_up = [&](std::string where, fs::path p) - { - for (auto itor=v.begin(); itor!=v.end(); ++itor) - { - if (fs::equivalent(p, itor->second)) - { - auto pair = std::move(*itor); - v.erase(itor); - v.push_back({where + ", was " + pair.first, p}); - return; - } - } - - v.push_back({where, p}); - }; - - - fs::path master; - - // auto detect from exe directory - if (auto_detect) - { - if (verbose) - { - const auto r = find_root(verbose); - u8cout << "root is " << path_to_utf8(r) << "\n"; - } - - master = find_in_root(default_ini_filename()); - - if (verbose) - u8cout << "found master " << path_to_utf8(master) << "\n"; - - v.push_back({"master", master}); - } - - - // MOBINI environment variable - if (auto e=this_env::get_opt("MOBINI")) - { - if (verbose) - u8cout << "found env MOBINI: '" << *e << "'\n"; - - for (auto&& i : split(*e, ";")) - { - if (verbose) - u8cout << "checking '" << i << "'\n"; - - auto p = fs::path(i); - if (!fs::exists(p)) - { - u8cerr << "ini from env MOBINI " << i << " not found\n"; - throw bailed(); - } - - p = fs::canonical(p); - - if (verbose) - u8cout << "ini from env: " << path_to_utf8(p) << "\n"; - - add_or_move_up("env", p); - } - } - - - // auto detect from the current directory - if (auto_detect) - { - MOB_ASSERT(!master.empty()); - - auto cwd = fs::current_path(); - - while (!cwd.empty()) { - const auto in_cwd = cwd / default_ini_filename(); - if (fs::exists(in_cwd) && !fs::equivalent(in_cwd, master)) - { - if (verbose) - u8cout << "also found in cwd " << path_to_utf8(in_cwd) << "\n"; - - v.push_back({ "cwd", fs::canonical(in_cwd) }); - break; - } - - const auto parent = cwd.parent_path(); - if (cwd == parent) - break; - - cwd = parent; - } - } - - - // command line - for (auto&& i : from_cl) - { - auto p = fs::path(i); - if (!fs::exists(p)) - { - u8cerr << "ini " << i << " not found\n"; - throw bailed(); - } - - p = fs::canonical(p); - - if (verbose) - u8cout << "ini from command line: " << path_to_utf8(p) << "\n"; - - add_or_move_up("cl", p); - } - - - if (verbose) - { - u8cout << "\nhigher number overrides lower\n"; - - for (std::size_t i=0; ienabled()) - return {}; - - auto iscc = conf().tool().get("iscc"); - if (iscc.is_absolute()) - { - if (!fs::exists(iscc)) - { - gcx().bail_out(context::conf, - "{} doesn't exist (from ini, absolute path)", iscc); - } - - return iscc; - } - - fs::path p = find_in_path(path_to_utf8(iscc)); - if (fs::exists(p)) - return fs::canonical(fs::absolute(p)); - - for (int v : {5, 6, 7, 8}) - { - const fs::path inno = fmt::format("inno setup {}", v); - - for (fs::path pf : {conf().path().pf_x86(), conf().path().pf_x64()}) - { - p = pf / inno / iscc; - if (fs::exists(p)) - return fs::canonical(fs::absolute(p)); - } - } - - gcx().bail_out(context::conf, "can't find {} anywhere", iscc); -} - void init_options( const std::vector& inis, const std::vector& opts) { @@ -1227,6 +876,47 @@ fs::path make_temp_file() } +conf_global conf::global() +{ + return {}; +} + +conf_task conf::task(const std::vector& names) +{ + return {names}; +} + +conf_tools conf::tool() +{ + return {}; +} + +conf_transifex conf::transifex() +{ + return {}; +} + +conf_prebuilt conf::prebuilt() +{ + return {}; +} + +conf_versions conf::version() +{ + return {}; +} + +conf_paths conf::path() +{ + return {}; +} + +bool conf::dry() +{ + return conf().global().dry(); +} + + conf_global::conf_global() : conf_section("global") { diff --git a/src/core/conf.h b/src/core/conf.h index 76aac07..0180d2c 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -3,8 +3,6 @@ namespace mob { -std::string default_ini_filename(); - std::vector find_inis( bool auto_detect, const std::vector& from_cl, bool verbose); @@ -17,6 +15,8 @@ void log_options(); void dump_available_options(); fs::path find_in_path(std::string_view exe); +fs::path find_root(bool verbose=false); +fs::path find_in_root(const fs::path& file); fs::path make_temp_file(); @@ -29,6 +29,13 @@ namespace details void set_string( std::string_view section, std::string_view key, std::string_view value); + + void add_string( + std::string_view section, std::string_view key, std::string_view value); + + void set_string_for_task( + std::string_view task_name, std::string_view section, + std::string_view key, std::string_view value); } diff --git a/src/core/ini.cpp b/src/core/ini.cpp new file mode 100644 index 0000000..488d20f --- /dev/null +++ b/src/core/ini.cpp @@ -0,0 +1,318 @@ +#include "pch.h" +#include "ini.h" +#include "context.h" +#include "env.h" +#include "conf.h" +#include "../tasks/tasks.h" +#include "../utility/string.h" + +namespace mob +{ + +std::string default_ini_filename() +{ + return "mob.ini"; +} + +parsed_option parse_option(const std::string& s) +{ + // task:section/key=value + // task: is optional + std::regex re(R"((?:(.+)\:)?(.+)/(.*)=(.*))"); + std::smatch m; + + if (!std::regex_match(s, m, re)) + { + gcx().bail_out(context::conf, + "bad option {}, must be [task:]section/key=value", s); + } + + std::string task = trim_copy(m[1].str()); + std::string section = trim_copy(m[2].str()); + std::string key = trim_copy(m[3].str()); + std::string value = trim_copy(m[4].str()); + + return {task, section, key, value}; +} + + + +template +void ini_error( + const fs::path& ini, std::size_t line, std::string_view f, Args&&... args) +{ + gcx().bail_out(context::conf, + "{}:{}: {}", + path_to_utf8(ini), (line + 1), + fmt::format(f, std::forward(args)...)); +} + +std::vector read_ini(const fs::path& ini) +{ + std::ifstream in(ini); + + std::vector lines; + + for (;;) + { + std::string line; + std::getline(in, line); + trim(line); + + if (!in) + break; + + lines.push_back(std::move(line)); + } + + if (in.bad()) + gcx().bail_out(context::conf, "failed to read ini {}", ini); + + return lines; +} + +void parse_section( + const fs::path& ini, std::size_t& i, + const std::vector& lines, + std::string_view task, std::string_view section, bool add) +{ + ++i; + + for (;;) + { + if (i >= lines.size() || lines[i][0] == '[') + break; + + const auto& line = lines[i]; + + if (line.empty() || line[0] == '#' || line[0] == ';') + { + ++i; + continue; + } + + const auto sep = line.find("="); + if (sep == std::string::npos) + ini_error(ini, i, "bad line '{}'", line); + + const std::string k = trim_copy(line.substr(0, sep)); + const std::string v = trim_copy(line.substr(sep + 1)); + + if (k.empty()) + ini_error(ini, i, "bad line '{}'", line); + + if (section == "aliases") + { + add_alias(k, split_quoted(v, " ")); + } + else if (task.empty()) + { + if (add) + details::add_string(section, k, v); + else + details::set_string(section, k, v); + } + else + { + if (task == "_override") + { + details::set_string_for_task("_override", section, k, v); + } + else + { + const auto& tasks = find_tasks(task); + + if (tasks.empty()) + ini_error(ini, i, "no task matching '{}' found", task); + + for (auto& t : tasks) + details::set_string_for_task(t->name(), section, k, v); + } + } + + ++i; + } +} + +void parse_ini(const fs::path& ini, bool add) +{ + gcx().debug(context::conf, "using ini at {}", ini); + + const auto lines = read_ini(ini); + std::size_t i = 0; + + for (;;) + { + if (i >= lines.size()) + break; + + const auto& line = lines[i]; + if (line.empty() || line[0] == '#' || line[0] == ';') + { + ++i; + continue; + } + + if (line.starts_with("[") && line.ends_with("]")) + { + const std::string s = line.substr(1, line.size() - 2); + + std::string task, section; + + const auto col = s.find(":"); + + if (col == std::string::npos) + { + section = s; + } + else + { + task = s.substr(0, col); + section = s.substr(col + 1); + } + + parse_section(ini, i, lines, task, section, add); + } + else + { + ini_error(ini, i, "bad line '{}'", line); + } + } +} + +std::vector find_inis( + bool auto_detect, const std::vector& from_cl, bool verbose) +{ + // the string is just for verbose + std::vector> v; + + // adds a path to the vector; if the path already exists, moves it to + // the last element + auto add_or_move_up = [&](std::string where, fs::path p) + { + for (auto itor=v.begin(); itor!=v.end(); ++itor) + { + if (fs::equivalent(p, itor->second)) + { + auto pair = std::move(*itor); + v.erase(itor); + v.push_back({where + ", was " + pair.first, p}); + return; + } + } + + v.push_back({where, p}); + }; + + + fs::path master; + + // auto detect from exe directory + if (auto_detect) + { + if (verbose) + { + const auto r = find_root(verbose); + u8cout << "root is " << path_to_utf8(r) << "\n"; + } + + master = find_in_root(default_ini_filename()); + + if (verbose) + u8cout << "found master " << path_to_utf8(master) << "\n"; + + v.push_back({"master", master}); + } + + + // MOBINI environment variable + if (auto e=this_env::get_opt("MOBINI")) + { + if (verbose) + u8cout << "found env MOBINI: '" << *e << "'\n"; + + for (auto&& i : split(*e, ";")) + { + if (verbose) + u8cout << "checking '" << i << "'\n"; + + auto p = fs::path(i); + if (!fs::exists(p)) + { + u8cerr << "ini from env MOBINI " << i << " not found\n"; + throw bailed(); + } + + p = fs::canonical(p); + + if (verbose) + u8cout << "ini from env: " << path_to_utf8(p) << "\n"; + + add_or_move_up("env", p); + } + } + + + // auto detect from the current directory + if (auto_detect) + { + MOB_ASSERT(!master.empty()); + + auto cwd = fs::current_path(); + + while (!cwd.empty()) { + const auto in_cwd = cwd / default_ini_filename(); + if (fs::exists(in_cwd) && !fs::equivalent(in_cwd, master)) + { + if (verbose) + u8cout << "also found in cwd " << path_to_utf8(in_cwd) << "\n"; + + v.push_back({ "cwd", fs::canonical(in_cwd) }); + break; + } + + const auto parent = cwd.parent_path(); + if (cwd == parent) + break; + + cwd = parent; + } + } + + + // command line + for (auto&& i : from_cl) + { + auto p = fs::path(i); + if (!fs::exists(p)) + { + u8cerr << "ini " << i << " not found\n"; + throw bailed(); + } + + p = fs::canonical(p); + + if (verbose) + u8cout << "ini from command line: " << path_to_utf8(p) << "\n"; + + add_or_move_up("cl", p); + } + + + if (verbose) + { + u8cout << "\nhigher number overrides lower\n"; + + for (std::size_t i=0; i + @@ -125,6 +126,7 @@ + diff --git a/vs/mob.vcxproj.filters b/vs/mob.vcxproj.filters index 6e1dffb..421ec1c 100644 --- a/vs/mob.vcxproj.filters +++ b/vs/mob.vcxproj.filters @@ -195,6 +195,9 @@ src\core + + src\core + @@ -257,5 +260,8 @@ src\core + + src\core + \ No newline at end of file