mirror of
https://github.com/ModOrganizer2/mob.git
synced 2026-07-27 14:07:05 -07:00
820 lines
17 KiB
C++
820 lines
17 KiB
C++
#include "pch.h"
|
|
#include "conf.h"
|
|
#include "utility.h"
|
|
#include "context.h"
|
|
#include "process.h"
|
|
|
|
namespace mob
|
|
{
|
|
|
|
using string_map = std::map<std::string, std::string>;
|
|
using path_map = std::map<std::string, fs::path>;
|
|
using bool_map = std::map<std::string, bool>;
|
|
|
|
static fs::path g_ini;
|
|
const std::string ini_filename = "mob.ini";
|
|
|
|
// special case for options that are used often to avoid string manipulations
|
|
static bool g_dry = false;
|
|
static int g_log = 3;
|
|
static bool g_redownload = false;
|
|
static bool g_reextract = false;
|
|
static bool g_rebuild = false;
|
|
|
|
static string_map g_options =
|
|
{
|
|
{"mo_org", "ModOrganizer2"},
|
|
{"mo_branch", "master"}
|
|
};
|
|
|
|
static path_map g_third_party =
|
|
{
|
|
{"sevenz", ""},
|
|
{"jom", ""},
|
|
{"patch", ""},
|
|
{"nuget", ""},
|
|
{"vswhere", ""},
|
|
};
|
|
|
|
static bool_map g_prebuilt =
|
|
{
|
|
{"boost", false}
|
|
};
|
|
|
|
static string_map g_versions =
|
|
{
|
|
{"vs", ""},
|
|
{"vs_year", ""},
|
|
{"vs_toolset", ""},
|
|
{"sdk", ""},
|
|
{"sevenzip", ""},
|
|
{"zlib", ""},
|
|
{"boost", ""},
|
|
{"boost_vs", ""},
|
|
{"python", ""},
|
|
{"fmt", ""},
|
|
{"gtest", ""},
|
|
{"libbsarch", ""},
|
|
{"libloot", ""},
|
|
{"libloot_hash", ""},
|
|
{"openssl", ""},
|
|
{"bzip2", ""},
|
|
{"lz4", ""},
|
|
{"nmm", ""},
|
|
{"spdlog", ""},
|
|
{"usvfs", ""},
|
|
{"qt", ""},
|
|
{"qt_vs", ""},
|
|
{"pyqt", ""},
|
|
{"pyqt_builder", ""},
|
|
{"sip", ""},
|
|
{"pyqt_sip", ""},
|
|
};
|
|
|
|
static path_map g_paths =
|
|
{
|
|
{"third_party" , ""},
|
|
{"prefix", ""},
|
|
{"cache", ""},
|
|
{"build", ""},
|
|
{"install", ""},
|
|
{"install_bin", ""},
|
|
{"install_libs", ""},
|
|
{"install_pdbs", ""},
|
|
{"install_dlls", ""},
|
|
{"install_loot", ""},
|
|
{"install_plugins", ""},
|
|
{"patches", ""},
|
|
{"vs", ""},
|
|
{"vcvars", ""},
|
|
{"qt_install", ""},
|
|
{"qt_bin", ""},
|
|
{"pf_x86", ""},
|
|
{"pf_x64", ""},
|
|
{"temp_dir", ""},
|
|
};
|
|
|
|
|
|
template <class Map>
|
|
const typename Map::mapped_type& get(
|
|
const std::string& map_name, const Map& map,
|
|
const std::string& name)
|
|
{
|
|
auto itor = map.find(name);
|
|
|
|
if (itor == map.end())
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
map_name + " '" + name + "' doesn't exist");
|
|
}
|
|
|
|
return itor->second;
|
|
}
|
|
|
|
|
|
const std::string& conf::by_name(const std::string& name)
|
|
{
|
|
return get("option", g_options, name);
|
|
}
|
|
|
|
const fs::path& third_party::by_name(const std::string& s)
|
|
{
|
|
return get("third party", g_third_party, s);
|
|
}
|
|
|
|
bool prebuilt::by_name(const std::string& s)
|
|
{
|
|
return get("prebuilt", g_prebuilt, s);
|
|
}
|
|
|
|
const std::string& versions::by_name(const std::string& s)
|
|
{
|
|
return get("version", g_versions, s);
|
|
}
|
|
|
|
const fs::path& paths::by_name(const std::string& s)
|
|
{
|
|
return get("path", g_paths, s);
|
|
}
|
|
|
|
|
|
bool conf::log_dump() { return g_log > 5; }
|
|
bool conf::log_trace() { return g_log > 4; }
|
|
bool conf::log_debug() { return g_log > 3; }
|
|
bool conf::log_info() { return g_log > 2; }
|
|
bool conf::log_warning() { return g_log > 1; }
|
|
bool conf::log_error() { return g_log > 0; }
|
|
bool conf::dry() { return g_dry; }
|
|
bool conf::redownload() { return g_redownload; }
|
|
bool conf::reextract() { return g_reextract; }
|
|
bool conf::rebuild() { return g_rebuild; }
|
|
|
|
|
|
fs::path tool_paths::perl() { return "perl.exe"; }
|
|
fs::path tool_paths::msbuild() { return "msbuild.exe"; }
|
|
fs::path tool_paths::devenv() { return "devenv.exe"; }
|
|
fs::path tool_paths::cmake() { return "cmake.exe"; }
|
|
fs::path tool_paths::git() { return "git.exe"; }
|
|
|
|
|
|
void conf_command_line_options(clipp::group& g)
|
|
{
|
|
g.push_back(
|
|
(clipp::option("-i", "--ini")
|
|
>> [&](auto&& v){ g_ini = v; })
|
|
% ("path to the ini file (defaults to ./" + ini_filename + ")"),
|
|
|
|
(clipp::option("--dry")
|
|
>> [&]{ g_dry = true; })
|
|
% "simulates filesystem operations",
|
|
|
|
(clipp::option("-l", "--log-level")
|
|
& clipp::value("level", g_log))
|
|
% "0 is silent, 6 is max",
|
|
|
|
//(clipp::option("-o", "--option")
|
|
// & clipp::value("key=value")
|
|
// >> [&](auto&& v){ set_opt(v); })
|
|
// % "sets an option",
|
|
|
|
(clipp::option("-g", "--redownload")
|
|
>> [&]{ g_redownload = true; })
|
|
% "redownloads archives, see --reextract",
|
|
|
|
(clipp::option("-e", "--reextract")
|
|
>> [&]{ g_reextract = true; })
|
|
% "deletes source directories and re-extracts archives",
|
|
|
|
(clipp::option("-b", "--rebuild")
|
|
>> [&]{ g_rebuild = true; })
|
|
% "cleans and rebuilds projects",
|
|
|
|
(clipp::option("-c", "--clean")
|
|
>> [&]{ g_redownload=true; g_reextract=true; g_rebuild=true; })
|
|
% "combines --redownload, --reextract and --rebuild"
|
|
);
|
|
}
|
|
|
|
|
|
bool try_parts(fs::path& check, const std::vector<std::string>& parts)
|
|
{
|
|
for (std::size_t i=0; i<parts.size() - 1; ++i)
|
|
{
|
|
fs::path p = check;
|
|
|
|
for (std::size_t j=i; j<parts.size(); ++j)
|
|
p /= parts[j];
|
|
|
|
if (fs::exists(p))
|
|
{
|
|
check = p;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
fs::path find_root_impl()
|
|
{
|
|
gcx().trace(context::conf, "looking for root directory");
|
|
|
|
fs::path p = fs::current_path();
|
|
|
|
if (try_parts(p, {"..", "..", "..", "third-party"}))
|
|
return p;
|
|
|
|
gcx().bail_out(context::conf, "root directory not found");
|
|
}
|
|
|
|
fs::path find_root()
|
|
{
|
|
const auto p = find_root_impl().parent_path();
|
|
|
|
gcx().trace(context::conf, "found root directory at " + p.string());
|
|
|
|
return p;
|
|
}
|
|
|
|
fs::path find_in_root(const fs::path& file)
|
|
{
|
|
static fs::path root = find_root();
|
|
|
|
fs::path p = root / file;
|
|
if (!fs::exists(p))
|
|
gcx().bail_out(context::conf, p.string() + " not found");
|
|
|
|
gcx().trace(context::conf, "found " + p.string());
|
|
return p;
|
|
}
|
|
|
|
|
|
fs::path find_third_party_directory()
|
|
{
|
|
static fs::path path = find_in_root("third-party");
|
|
return path;
|
|
}
|
|
|
|
|
|
fs::path find_in_path(const std::string& exe)
|
|
{
|
|
const std::size_t buffer_size = MAX_PATH;
|
|
char buffer[buffer_size + 1] = {};
|
|
|
|
if (SearchPathA(nullptr, exe.c_str(), nullptr, buffer_size, buffer, nullptr))
|
|
return buffer;
|
|
else
|
|
return {};
|
|
}
|
|
|
|
bool find_qmake(fs::path& check)
|
|
{
|
|
// try Qt/Qt5.14.2/msvc*/bin/qmake.exe
|
|
if (try_parts(check, {
|
|
"Qt",
|
|
"Qt" + versions::qt(),
|
|
"msvc" + versions::qt_vs() + "_64",
|
|
"bin",
|
|
"qmake.exe"}))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// try Qt/5.14.2/msvc*/bin/qmake.exe
|
|
if (try_parts(check, {
|
|
"Qt",
|
|
versions::qt(),
|
|
"msvc" + versions::qt_vs() + "_64",
|
|
"bin",
|
|
"qmake.exe"}))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool try_qt_location(fs::path& check)
|
|
{
|
|
if (!find_qmake(check))
|
|
return false;
|
|
|
|
check = fs::absolute(check.parent_path() / "..");
|
|
return true;
|
|
}
|
|
|
|
fs::path find_qt()
|
|
{
|
|
fs::path p = g_paths["qt_install"];
|
|
|
|
if (!p.empty())
|
|
{
|
|
p = fs::absolute(p);
|
|
|
|
if (!try_qt_location(p))
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"no qt install in " + p.string());
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
std::vector<fs::path> locations =
|
|
{
|
|
paths::pf_x64(),
|
|
"C:",
|
|
"D:"
|
|
};
|
|
|
|
// look for qmake, which is in %qt%/version/msvc.../bin
|
|
fs::path qmake = find_in_path("qmake.exe");
|
|
if (!qmake.empty())
|
|
locations.insert(locations.begin(), qmake.parent_path() / "../../");
|
|
|
|
// look for qtcreator.exe, which is in %qt%/Tools/QtCreator/bin
|
|
fs::path qtcreator = find_in_path("qtcreator.exe");
|
|
if (!qtcreator.empty())
|
|
locations.insert(locations.begin(), qtcreator.parent_path() / "../../../");
|
|
|
|
for (fs::path loc : locations)
|
|
{
|
|
loc = fs::absolute(loc);
|
|
if (try_qt_location(loc))
|
|
return loc;
|
|
}
|
|
|
|
gcx().bail_out(context::conf, "can't find qt install");
|
|
}
|
|
|
|
void validate_qt()
|
|
{
|
|
fs::path p = paths::qt_install();
|
|
|
|
if (!try_qt_location(p))
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"qt path " + p.string() + " doesn't exist\n");
|
|
}
|
|
|
|
g_paths["qt_install"] = p;
|
|
}
|
|
|
|
fs::path get_known_folder(const GUID& id)
|
|
{
|
|
wchar_t* buffer = nullptr;
|
|
const auto r = ::SHGetKnownFolderPath(id, 0, 0, &buffer);
|
|
|
|
if (r != S_OK)
|
|
return {};
|
|
|
|
fs::path p = buffer;
|
|
::CoTaskMemFree(buffer);
|
|
|
|
return p;
|
|
}
|
|
|
|
fs::path find_program_files_x86()
|
|
{
|
|
fs::path p = get_known_folder(FOLDERID_ProgramFilesX86);
|
|
|
|
if (p.empty())
|
|
{
|
|
const auto e = GetLastError();
|
|
|
|
p = fs::path(R"(C:\Program Files (x86))");
|
|
|
|
gcx().warning(context::conf,
|
|
"failed to get x86 program files folder, defaulting to " +
|
|
p.string(), e);
|
|
}
|
|
else
|
|
{
|
|
gcx().trace(context::conf,
|
|
"x86 program files is " + p.string());
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
fs::path find_program_files_x64()
|
|
{
|
|
fs::path p = get_known_folder(FOLDERID_ProgramFilesX64);
|
|
|
|
if (p.empty())
|
|
{
|
|
const auto e = GetLastError();
|
|
|
|
p = fs::path(R"(C:\Program Files)");
|
|
|
|
gcx().warning(context::conf,
|
|
"failed to get x64 program files folder, defaulting to " +
|
|
p.string(), e);
|
|
}
|
|
else
|
|
{
|
|
gcx().trace(context::conf,
|
|
"x64 program files is " + p.string());
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
fs::path find_temp_dir()
|
|
{
|
|
const std::size_t buffer_size = MAX_PATH + 2;
|
|
wchar_t buffer[buffer_size] = {};
|
|
|
|
if (GetTempPathW(static_cast<DWORD>(buffer_size), buffer) == 0)
|
|
{
|
|
const auto e = GetLastError();
|
|
gcx().bail_out(context::conf, "can't get temp path", e);
|
|
}
|
|
|
|
fs::path p(buffer);
|
|
gcx().trace(context::conf, "temp dir is " + p.string());
|
|
|
|
return p;
|
|
}
|
|
|
|
fs::path find_vs()
|
|
{
|
|
auto p = process()
|
|
.binary(third_party::vswhere())
|
|
.arg("-prerelease")
|
|
.arg("-version", versions::vs())
|
|
.arg("-property", "installationPath")
|
|
.stdout_flags(process::keep_in_string)
|
|
.stderr_flags(process::inherit);
|
|
|
|
p.run();
|
|
p.join();
|
|
|
|
if (p.exit_code() != 0)
|
|
gcx().bail_out(context::conf, "vswhere failed");
|
|
|
|
fs::path path = trim_copy(p.steal_stdout());
|
|
|
|
if (!fs::exists(path))
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"the path given by vswhere doesn't exist: " + path.string());
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
fs::path find_vcvars()
|
|
{
|
|
const auto vcvars =
|
|
paths::vs() / "VC" / "Auxiliary" / "Build" / "vcvarsall.bat";
|
|
|
|
if (!fs::exists(vcvars))
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"can't find vcvars batch file at " + vcvars.string());
|
|
}
|
|
|
|
return vcvars;
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
bool parse_value(const std::string& s, T& out) = delete;
|
|
|
|
template <>
|
|
bool parse_value<bool>(const std::string& s, bool& out)
|
|
{
|
|
std::istringstream iss(s);
|
|
iss >> out;
|
|
return !iss.bad();
|
|
}
|
|
|
|
template <>
|
|
bool parse_value<std::string>(const std::string& s, std::string& out)
|
|
{
|
|
out = s;
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool parse_value<fs::path>(const std::string& s, fs::path& out)
|
|
{
|
|
out = s;
|
|
return true;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
fs::path find_ini()
|
|
{
|
|
fs::path p = g_ini;
|
|
|
|
if (!p.empty())
|
|
{
|
|
if (fs::exists(p))
|
|
return fs::canonical(p);
|
|
}
|
|
|
|
p = fs::current_path();
|
|
|
|
if (try_parts(p, {"..", "..", "..", ini_filename}))
|
|
return p;
|
|
|
|
gcx().bail_out(context::conf, "can't find " + ini_filename);
|
|
}
|
|
|
|
std::vector<std::string> read_ini(const fs::path& ini)
|
|
{
|
|
std::ifstream in(ini);
|
|
|
|
std::vector<std::string> lines;
|
|
|
|
for (;;)
|
|
{
|
|
std::string line;
|
|
std::getline(in, line);
|
|
trim(line);
|
|
|
|
if (!in)
|
|
break;
|
|
|
|
if (line.empty() || line[0] == '#' || line[0] == ';')
|
|
continue;
|
|
|
|
lines.push_back(std::move(line));
|
|
}
|
|
|
|
if (in.bad())
|
|
gcx().bail_out(context::conf, "failed to read ini " + ini.string());
|
|
|
|
return lines;
|
|
}
|
|
|
|
template <class Map>
|
|
void parse_section(
|
|
std::size_t& i, const std::vector<std::string>& lines, Map& map)
|
|
{
|
|
++i;
|
|
|
|
for (;;)
|
|
{
|
|
if (i >= lines.size() || lines[i][0] == '[')
|
|
break;
|
|
|
|
const auto& line = lines[i];
|
|
|
|
const auto sep = line.find("=");
|
|
if (sep == std::string::npos)
|
|
ini_error(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(i, "bad line '" + line + "'");
|
|
|
|
auto itor = map.find(k);
|
|
if (itor == map.end())
|
|
ini_error(i, "unknown key '" + k + "'");
|
|
|
|
if (!parse_value<typename Map::mapped_type>(v, itor->second))
|
|
ini_error(i, "bad line '" + line + "'");
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void parse_ini()
|
|
{
|
|
g_ini = find_ini();
|
|
gcx().debug(context::conf, "using ini at " + g_ini.string());
|
|
|
|
const auto lines = read_ini(g_ini);
|
|
std::size_t i = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (i >= lines.size())
|
|
break;
|
|
|
|
if (lines[i] == "[options]")
|
|
parse_section(i, lines, g_options);
|
|
else if (lines[i] == "[third-party]")
|
|
parse_section(i, lines, g_third_party);
|
|
else if (lines[i] == "[prebuilt]")
|
|
parse_section(i, lines, g_prebuilt);
|
|
else if (lines[i] == "[versions]")
|
|
parse_section(i, lines, g_versions);
|
|
else if (lines[i] == "[paths]")
|
|
parse_section(i, lines, g_paths);
|
|
else
|
|
ini_error(i, "bad section " + lines[i]);
|
|
}
|
|
}
|
|
|
|
void check_missing_options()
|
|
{
|
|
if (conf::mo_org().empty())
|
|
gcx().bail_out(context::conf, "missing mo_org in [options]");
|
|
|
|
if (conf::mo_branch().empty())
|
|
gcx().bail_out(context::conf, "missing mo_branch in [options]");
|
|
|
|
if (paths::prefix().empty())
|
|
gcx().bail_out(context::conf, "missing prefix in [paths]");
|
|
|
|
for (auto&& [k, v] : g_versions)
|
|
{
|
|
if (v.empty())
|
|
gcx().bail_out(context::conf, "missing version for " + k);
|
|
}
|
|
}
|
|
|
|
|
|
void find_in_third_party(const std::string& k, const std::string& exe)
|
|
{
|
|
auto itor = g_third_party.find(k);
|
|
|
|
if (itor == g_third_party.end())
|
|
gcx().bail_out(context::conf, "unknown third party key " + k);
|
|
|
|
if (!itor->second.empty())
|
|
{
|
|
if (fs::exists(itor->second))
|
|
{
|
|
itor->second = fs::canonical(itor->second);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"third party " + itor->second.string() + " not found");
|
|
}
|
|
}
|
|
|
|
auto p = paths::third_party() / "bin" / exe;
|
|
|
|
if (fs::exists(p))
|
|
{
|
|
itor->second = fs::canonical(p);
|
|
return;
|
|
}
|
|
|
|
p = find_in_path(exe);
|
|
if (fs::exists(p))
|
|
{
|
|
itor->second = fs::canonical(p);
|
|
return;
|
|
}
|
|
|
|
gcx().bail_out(context::conf, "can't find " + exe);
|
|
}
|
|
|
|
template <class F>
|
|
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 party key " + k);
|
|
|
|
if (!itor->second.empty())
|
|
{
|
|
if (fs::exists(itor->second))
|
|
{
|
|
itor->second = fs::canonical(itor->second);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"path " + itor->second.string() + " not found");
|
|
}
|
|
}
|
|
|
|
fs::path cp;
|
|
|
|
if constexpr (std::is_same_v<fs::path, std::decay_t<decltype(f)>>)
|
|
cp = fs::absolute(f);
|
|
else
|
|
cp = fs::absolute(f());
|
|
|
|
if (!fs::exists(cp))
|
|
{
|
|
gcx().bail_out(context::conf,
|
|
"path " + cp.string() + " not found");
|
|
}
|
|
|
|
itor->second = fs::canonical(cp);
|
|
}
|
|
|
|
|
|
void init_options()
|
|
{
|
|
parse_ini();
|
|
|
|
check_missing_options();
|
|
|
|
set_path_if_empty("third_party", find_third_party_directory);
|
|
|
|
find_in_third_party("sevenz", "7z.exe");
|
|
find_in_third_party("jom", "jom.exe");
|
|
find_in_third_party("patch", "patch.exe");
|
|
find_in_third_party("vswhere", "vswhere.exe");
|
|
find_in_third_party("nuget", "nuget.exe");
|
|
|
|
set_path_if_empty("vs", find_vs);
|
|
set_path_if_empty("vcvars", find_vcvars);
|
|
set_path_if_empty("qt_install", find_qt);
|
|
set_path_if_empty("pf_x86", find_program_files_x86);
|
|
set_path_if_empty("pf_x64", find_program_files_x64);
|
|
set_path_if_empty("temp_dir", find_temp_dir);
|
|
set_path_if_empty("patches", []{ return find_in_root("patches"); });
|
|
|
|
set_path_if_empty("cache", paths::prefix() / "downloads");
|
|
set_path_if_empty("build", paths::prefix() / "build");
|
|
set_path_if_empty("install", paths::prefix() / "install");
|
|
set_path_if_empty("install_bin", paths::install() / "bin");
|
|
set_path_if_empty("install_libs", paths::install() / "libs");
|
|
set_path_if_empty("install_pdbs", paths::install() / "pdbs");
|
|
set_path_if_empty("install_dlls", paths::install_bin() / "dlls");
|
|
set_path_if_empty("install_loot", paths::install_bin() / "loot");
|
|
set_path_if_empty("install_plugins", paths::install_bin() / "plugins");
|
|
set_path_if_empty("qt_bin", paths::qt_install() / "bin");
|
|
|
|
validate_qt();
|
|
}
|
|
|
|
template <class Map>
|
|
void table(const std::string& caption, const Map& values)
|
|
{
|
|
std::size_t longest = 0;
|
|
for (auto&& [k, v] : values)
|
|
longest = std::max(longest, k.size());
|
|
|
|
gcx().trace(context::conf, caption + ":");
|
|
for (auto&& [k, v] : values)
|
|
gcx().trace(context::conf, " . " + pad_right(k, longest) + " = " + v);
|
|
}
|
|
|
|
void dump_options()
|
|
{
|
|
const string_map manual_opts =
|
|
{
|
|
{"dry", g_dry ? "true" : "false"},
|
|
{"log", std::to_string(g_log)},
|
|
{"redownload", g_redownload ? "true" : "false"},
|
|
{"reextract", g_reextract ? "true" : "false"},
|
|
{"rebuild", g_rebuild ? "true" : "false"},
|
|
};
|
|
|
|
string_map opts = g_options;
|
|
opts.insert(manual_opts.begin(), manual_opts.end());
|
|
table("options", g_options);
|
|
|
|
string_map third_party;
|
|
for (auto&& [k, v] : g_third_party)
|
|
third_party[k] = v.string();
|
|
table("third-party", third_party);
|
|
|
|
string_map prebuilt;
|
|
for (auto&& [k, v] : g_prebuilt)
|
|
prebuilt[k] = (v ? "true" : "false");
|
|
table("prebuilt", prebuilt);
|
|
|
|
table("versions", g_versions);
|
|
|
|
string_map paths;
|
|
for (auto&& [k, v] : g_paths)
|
|
paths[k] = v.string();
|
|
table("paths", paths);
|
|
}
|
|
|
|
|
|
fs::path make_temp_file()
|
|
{
|
|
static fs::path dir = paths::temp_dir();
|
|
|
|
wchar_t name[MAX_PATH + 1] = {};
|
|
if (GetTempFileNameW(dir.native().c_str(), L"mob", 0, name) == 0)
|
|
{
|
|
const auto e = GetLastError();
|
|
|
|
gcx().bail_out(context::conf,
|
|
"can't create temp file in " + dir.string(), e);
|
|
}
|
|
|
|
return dir / name;
|
|
}
|
|
|
|
} // namespace
|