From 2de230cf11d14392941ee99114f0bf709881c626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sun, 9 Jun 2024 16:57:51 +0200 Subject: [PATCH] Allow building configurations other than RelWithDebInfo. --- mob.ini | 15 ++--- readme.md | 9 +-- src/core/conf.cpp | 109 ++++++++++++++++++++++++------------- src/core/conf.h | 85 ++++++++++------------------- src/tasks/gtest.cpp | 40 +++++++------- src/tasks/modorganizer.cpp | 46 +++++++++------- src/tasks/tasks.h | 9 +-- src/tasks/usvfs.cpp | 9 ++- src/tools/cmake.cpp | 2 +- src/tools/cmake.h | 6 +- src/tools/msbuild.cpp | 24 ++++++-- src/tools/msbuild.h | 16 ++++-- src/utility.h | 2 + 13 files changed, 208 insertions(+), 164 deletions(-) diff --git a/mob.ini b/mob.ini index 29160ce..5fe874f 100644 --- a/mob.ini +++ b/mob.ini @@ -22,13 +22,14 @@ super = cmake_common modorganizer* githubpp plugins = check_fnis bsapacker bsa_extractor diagnose_basic installer_* plugin_python preview_base preview_bsa tool_* game_* [task] -enabled = true -mo_org = ModOrganizer2 -mo_branch = master -mo_fallback = -no_pull = false -ignore_ts = false -revert_ts = false +enabled = true +mo_org = ModOrganizer2 +mo_branch = master +mo_fallback = +no_pull = false +ignore_ts = false +revert_ts = false +configuration = RelWithDebInfo git_url_prefix = https://github.com/ git_shallow = true diff --git a/readme.md b/readme.md index ffc9cb0..99ecc0f 100644 --- a/readme.md +++ b/readme.md @@ -68,7 +68,7 @@ aqt install-qt --outputdir "C:\Qt" windows desktop 6.7.0 win64_msvc2019_64 -m qt - Optional: - Qt Source Files - Qt Debug Files - + ### Visual Studio - Install Visual Studio 2022 ([Installer](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false)) - Desktop development with C++ @@ -141,9 +141,10 @@ Inside the INI file are `[sections]` and `key = value` pairs. The `[task]` secti ### `[task]` Options for individual tasks. Can be `[task_name:task]`, where `task_name` is the name of a task (see `mob list`) , `super` for all MO tasks or a glob like `installer_*`. -| Option | Type | Description | -| --- | --- | --- | -| `enabled` | bool | Whether this task is enabled. Disabled tasks are never built. When specifying task names with `mob build task1 task2...`, all tasks except those given are turned off. | +| Option | Type | Description | +| --- | --- | --- | +| `enabled` | bool | Whether this task is enabled. Disabled tasks are never built. When specifying task names with `mob build task1 task2...`, all tasks except those given are turned off. | +| `configuration` | enum | Which configuration to build, should be one of Debug, Release or RelWithDebInfo with RelWithDebInfo being the default.| #### Common git options Unless otherwise stated, applies to any task that is a git repo. diff --git a/src/core/conf.cpp b/src/core/conf.cpp index acaa076..fe3d152 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -184,6 +184,40 @@ namespace mob::details { g_tasks[task_name][key] = std::move(value); } + // check if the two given string are equals case-insensitive + // + bool case_insensitive_equals(std::string_view lhs, std::string_view rhs) + { + // _strcmpi does not have a n-overload, and since string_view is not + // necessarily null-terminated, _strmcpi cannot be safely used + return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs), + std::end(rhs), [](auto&& c1, auto&& c2) { + return ::tolower(c1) == ::tolower(c2); + }); + } + + // read a CMake constant from the configuration + // + template + T parse_cmake_value(std::string_view section, std::string_view key, + std::string_view value, + std::unordered_map const& values) + { + for (const auto& [value_c, value_s] : values) { + if (case_insensitive_equals(value_s, value)) { + return value_c; + } + } + + // build a string containing allowed value for logging + std::vector values_s; + for (const auto& [value_c, value_s] : values) { + values_s.push_back(value_s); + } + gcx().bail_out(context::conf, "bad value '{}' for {}/{} (expected one of {})", + value, section, key, join(values_s, ", ", std::string{})); + } + } // namespace mob::details namespace mob { @@ -666,6 +700,34 @@ namespace mob { return details::g_dry; } + // use appropriate case for the below constants since we will be using them in + // to_string, although most of cmake and msbuild is case-insensitive so it will + // not matter much in the end + + static std::unordered_map constant_values{ + {conf_cmake::always, "ALWAYS"}, + {conf_cmake::lazy, "LAZY"}, + {conf_cmake::never, "NEVER"}}; + + std::string conf_cmake::to_string(constant c) + { + return std::string{constant_values.at(c)}; + } + + conf_cmake::conf_cmake() : conf_section("cmake") {} + + conf_cmake::constant conf_cmake::install_message() const + { + return details::parse_cmake_value( + name(), "install_message", details::get_string(name(), "install_message"), + constant_values); + } + + std::string conf_cmake::host() const + { + return details::get_string(name(), "host"); + } + conf_task::conf_task(std::vector names) : names_(std::move(names)) {} std::string conf_task::get(std::string_view key) const @@ -678,45 +740,16 @@ namespace mob { return details::get_bool_for_task(names_, key); } - bool conf_cmake::cmake_constant::is_equivalent(std::string_view other) const + mob::config conf_task::configuration() const { - // _strcmpi does not have a n-overload, and since string_view is not - // necessarily null-terminated, _strmcpi cannot be safely used - return std::equal(std::begin(value_), std::end(value_), std::begin(other), - std::end(other), [](auto&& c1, auto&& c2) { - return ::tolower(c1) == ::tolower(c2); - }); - } - - conf_cmake::conf_cmake() : conf_section("cmake") {} - - const conf_cmake::cmake_constant conf_cmake::ALWAYS{"always"}; - const conf_cmake::cmake_constant conf_cmake::LAZY{"lazy"}; - const conf_cmake::cmake_constant conf_cmake::NEVER{"never"}; - - conf_cmake::cmake_constant conf_cmake::install_message() const - { - return read_cmake_constant("install_message", {ALWAYS, LAZY, NEVER}); - } - - std::string conf_cmake::host() const - { - return details::get_string(name(), "host"); - } - - conf_cmake::cmake_constant - conf_cmake::read_cmake_constant(std::string_view key, - std::vector const& allowed) const - { - const auto value = details::get_string(name(), key); - for (const auto& constant : allowed) { - if (constant.is_equivalent(value)) { - return constant; - } - } - - gcx().bail_out(context::conf, "bad value '{}' for {}/{} (expected one of {})", - value, name(), key, join(allowed, ", ", std::string{})); + static std::unordered_map configuration_values{ + {mob::config::release, "Release"}, + {mob::config::debug, "Debug"}, + {mob::config::relwithdebinfo, "RelWithDebInfo"}}; + return details::parse_cmake_value( + names_[0], "configuration", + details::get_string_for_task(names_, "configuration"), + configuration_values); } conf_tools::conf_tools() : conf_section("tools") {} diff --git a/src/core/conf.h b/src/core/conf.h index 891fc83..c669b20 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -1,5 +1,7 @@ #pragma once +#include "../utility.h" + // these shouldn't be called directly, they're used by some of the template // below // @@ -102,6 +104,29 @@ namespace mob { bool build() const { return get("build_task"); } }; + // options in [cmake] + // + class conf_cmake : conf_section { + public: + enum class constant { always, lazy, never }; + using enum constant; + + static std::string to_string(constant m); + + conf_cmake(); + + // specify the value for CMAKE_INSTALL_MESSAGE + // + constant install_message() const; + + // specify the toolset host configuration, if any, this is equivalent + // to -T host=XXX on the command line + // + // an empty string means no host configured + // + std::string host() const; + }; + // options in [task] or [task_name:task] // class conf_task { @@ -141,64 +166,16 @@ namespace mob { return get("remote_push_default_origin"); } + // specify the configuration to build + // + mob::config configuration() const; + private: std::vector names_; bool get_bool(std::string_view name) const; }; - // options in [task] or [task_name:task] - // - class conf_cmake : conf_section { - public: - class cmake_constant { - std::string value_; - - // check if the given string is equivalent to this constant - // - bool is_equivalent(std::string_view other) const; - - friend class conf_cmake; - - public: - constexpr cmake_constant(std::string_view value) : value_{value} {} - constexpr const auto& value() const { return value_; } - constexpr operator const std::string&() const { return value(); } - - friend bool operator==(cmake_constant const& lhs, cmake_constant const& rhs) - { - return lhs.is_equivalent(rhs.value()); - } - friend bool operator!=(cmake_constant const& lhs, cmake_constant const& rhs) - { - return !(lhs == rhs); - } - }; - - static const cmake_constant ALWAYS; - static const cmake_constant LAZY; - static const cmake_constant NEVER; - - public: - conf_cmake(); - - // specify the value for CMAKE_INSTALL_MESSAGE - // - cmake_constant install_message() const; - - // specify the toolset host configuration, if any, this is equivalent - // to -T host=XXX on the command line - // - // an empty string means no host configured - // - std::string host() const; - - private: - cmake_constant - read_cmake_constant(std::string_view key, - std::vector const& allowed) const; - }; - // options in [tools] // class conf_tools : public conf_section { @@ -234,9 +211,7 @@ namespace mob { conf_paths(); #define VALUE(NAME) \ - fs::path NAME() const \ - { \ - return get(#NAME); \ + fs::path NAME() const {return get(#NAME); \ } VALUE(third_party); diff --git a/src/tasks/gtest.cpp b/src/tasks/gtest.cpp index 93766c8..359a433 100644 --- a/src/tasks/gtest.cpp +++ b/src/tasks/gtest.cpp @@ -5,8 +5,7 @@ namespace mob::tasks { namespace { - cmake create_cmake_tool(arch a, const std::string& config, - cmake::ops o = cmake::generate) + cmake create_cmake_tool(arch a, config config, cmake::ops o = cmake::generate) { return std::move(cmake(o) .generator(cmake::vs) @@ -17,12 +16,12 @@ namespace mob::tasks { .root(gtest::source_path())); } - msbuild create_msbuild_tool(arch a, std::string const& config, + msbuild create_msbuild_tool(arch a, config config, msbuild::ops o = msbuild::build) { const fs::path build_path = create_cmake_tool(a, config).build_path(); - return std::move(msbuild(o).architecture(a).config(config).solution( + return std::move(msbuild(o).architecture(a).configuration(config).solution( build_path / "INSTALL.vcxproj")); } @@ -45,9 +44,10 @@ namespace mob::tasks { return conf().path().build() / "googletest"; } - fs::path gtest::build_path(arch a, const std::string& c) + fs::path gtest::build_path(arch a, config c) { - return source_path() / "build" / (a == arch::x64 ? "x64" : "Win32") / c; + return source_path() / "build" / (a == arch::x64 ? "x64" : "Win32") / + msbuild::configuration_name(c); } void gtest::do_clean(clean c) @@ -58,15 +58,15 @@ namespace mob::tasks { } if (is_set(c, clean::reconfigure)) { - run_tool(create_cmake_tool(arch::x86, "Release", cmake::clean)); - run_tool(create_cmake_tool(arch::x64, "Release", cmake::clean)); + run_tool(create_cmake_tool(arch::x86, config::release, cmake::clean)); + run_tool(create_cmake_tool(arch::x64, config::release, cmake::clean)); } if (is_set(c, clean::rebuild)) { - run_tool(create_msbuild_tool(arch::x86, "Release", msbuild::clean)); - run_tool(create_msbuild_tool(arch::x86, "Debug", msbuild::clean)); - run_tool(create_msbuild_tool(arch::x64, "Release", msbuild::clean)); - run_tool(create_msbuild_tool(arch::x64, "Debug", msbuild::clean)); + run_tool(create_msbuild_tool(arch::x86, config::release, msbuild::clean)); + run_tool(create_msbuild_tool(arch::x86, config::debug, msbuild::clean)); + run_tool(create_msbuild_tool(arch::x64, config::release, msbuild::clean)); + run_tool(create_msbuild_tool(arch::x64, config::debug, msbuild::clean)); } } @@ -85,19 +85,19 @@ namespace mob::tasks { parallel({{"gtest64", [&] { - run_tool(create_cmake_tool(arch::x64, "Release")); - run_tool(create_msbuild_tool(arch::x64, "Release")); + run_tool(create_cmake_tool(arch::x64, config::release)); + run_tool(create_msbuild_tool(arch::x64, config::release)); - run_tool(create_cmake_tool(arch::x64, "Debug")); - run_tool(create_msbuild_tool(arch::x64, "Debug")); + run_tool(create_cmake_tool(arch::x64, config::debug)); + run_tool(create_msbuild_tool(arch::x64, config::debug)); }}, {"gtest32", [&] { - run_tool(create_cmake_tool(arch::x86, "Release")); - run_tool(create_msbuild_tool(arch::x86, "Release")); + run_tool(create_cmake_tool(arch::x86, config::release)); + run_tool(create_msbuild_tool(arch::x86, config::release)); - run_tool(create_cmake_tool(arch::x86, "Debug")); - run_tool(create_msbuild_tool(arch::x86, "Debug")); + run_tool(create_cmake_tool(arch::x86, config::debug)); + run_tool(create_msbuild_tool(arch::x86, config::debug)); }}}); } diff --git a/src/tasks/modorganizer.cpp b/src/tasks/modorganizer.cpp index 2ab2278..2678c87 100644 --- a/src/tasks/modorganizer.cpp +++ b/src/tasks/modorganizer.cpp @@ -200,36 +200,40 @@ namespace mob::tasks { cmake modorganizer::create_cmake_tool(cmake::ops o) { - return create_cmake_tool(source_path(), o); + return create_cmake_tool(source_path(), o, task_conf().configuration()); } - cmake modorganizer::create_cmake_tool(const fs::path& root, cmake::ops o) + cmake modorganizer::create_cmake_tool(const fs::path& root, cmake::ops o, config c) { - return std::move(cmake(o) - .generator(cmake::vs) - .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) - .def("DEPENDENCIES_DIR", conf().path().build()) - .def("BOOST_ROOT", boost::source_path()) - .def("BOOST_LIBRARYDIR", boost::lib_path(arch::x64)) - .def("SPDLOG_ROOT", spdlog::source_path()) - .def("LOOT_PATH", libloot::source_path()) - .def("LZ4_ROOT", lz4::source_path()) - .def("QT_ROOT", qt::installation_path()) - .def("ZLIB_ROOT", zlib::source_path()) - .def("PYTHON_ROOT", python::source_path()) - .def("SEVENZ_ROOT", sevenz::source_path()) - .def("LIBBSARCH_ROOT", libbsarch::source_path()) - .def("BOOST_DI_ROOT", boost_di::source_path()) - .def("GTEST_ROOT", gtest::build_path()) - .def("OPENSSL_ROOT_DIR", openssl::source_path()) - .root(root)); + return std::move( + cmake(o) + .generator(cmake::vs) + .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) + .def("DEPENDENCIES_DIR", conf().path().build()) + .def("BOOST_ROOT", boost::source_path()) + .def("BOOST_LIBRARYDIR", boost::lib_path(arch::x64)) + .def("SPDLOG_ROOT", spdlog::source_path()) + .def("LOOT_PATH", libloot::source_path()) + .def("LZ4_ROOT", lz4::source_path()) + .def("QT_ROOT", qt::installation_path()) + .def("ZLIB_ROOT", zlib::source_path()) + .def("PYTHON_ROOT", python::source_path()) + .def("SEVENZ_ROOT", sevenz::source_path()) + .def("LIBBSARCH_ROOT", libbsarch::source_path()) + .def("BOOST_DI_ROOT", boost_di::source_path()) + // gtest has no RelWithDebInfo, so simply use Debug/Release + .def("GTEST_ROOT", + gtest::build_path(arch::x64, c == config::debug ? config::debug + : config::release)) + .def("OPENSSL_ROOT_DIR", openssl::source_path()) + .root(root)); } msbuild modorganizer::create_msbuild_tool(msbuild::ops o) { return std::move(msbuild(o) .solution(project_file_path()) - .config("RelWithDebInfo") + .configuration(task_conf().configuration()) .architecture(arch::x64)); } diff --git a/src/tasks/tasks.h b/src/tasks/tasks.h index 0eed293..f1bf022 100644 --- a/src/tasks/tasks.h +++ b/src/tasks/tasks.h @@ -97,8 +97,7 @@ namespace mob::tasks { static std::string version(); static bool prebuilt(); static fs::path source_path(); - static fs::path build_path(arch arch = arch::x64, - const std::string& config = "Release"); + static fs::path build_path(arch = arch::x64, config = config::release); protected: void do_clean(clean c) override; @@ -199,7 +198,8 @@ namespace mob::tasks { // projects, used internally, but also by the cmake command // static cmake create_cmake_tool(const fs::path& root, - cmake::ops o = cmake::generate); + cmake::ops o = cmake::generate, + config config = config::relwithdebinfo); // flags for some MO projects enum flags { @@ -651,7 +651,8 @@ namespace mob::tasks { void fetch_from_source(); void build_and_install_from_source(); - msbuild create_msbuild_tool(arch a, msbuild::ops o = msbuild::build) const; + msbuild create_msbuild_tool(arch, msbuild::ops = msbuild::build, + config = config::release) const; }; class zlib : public basic_task { diff --git a/src/tasks/usvfs.cpp b/src/tasks/usvfs.cpp index 0263cf4..a61e277 100644 --- a/src/tasks/usvfs.cpp +++ b/src/tasks/usvfs.cpp @@ -42,8 +42,10 @@ namespace mob::tasks { if (is_set(c, clean::rebuild)) { // msbuild clean - run_tool(create_msbuild_tool(arch::x86, msbuild::clean)); - run_tool(create_msbuild_tool(arch::x64, msbuild::clean)); + run_tool(create_msbuild_tool(arch::x86, msbuild::clean, + task_conf().configuration())); + run_tool(create_msbuild_tool(arch::x64, msbuild::clean, + task_conf().configuration())); } } @@ -71,7 +73,7 @@ namespace mob::tasks { run_tool(create_msbuild_tool(arch::x64)); } - msbuild usvfs::create_msbuild_tool(arch a, msbuild::ops o) const + msbuild usvfs::create_msbuild_tool(arch a, msbuild::ops o, config c) const { // usvfs doesn't use "Win32" for 32-bit, it uses "x86" // @@ -90,6 +92,7 @@ namespace mob::tasks { return std::move( msbuild(o) .platform(plat) + .configuration(c) .targets({"usvfs_proxy"}) .solution(source_path() / "vsbuild" / "usvfs.sln") .env(env::vs(a) diff --git a/src/tools/cmake.cpp b/src/tools/cmake.cpp index 1bf83a6..0f277e0 100644 --- a/src/tools/cmake.cpp +++ b/src/tools/cmake.cpp @@ -129,7 +129,7 @@ namespace mob { .binary(binary()) .arg("-DCMAKE_BUILD_TYPE=Release") .arg("-DCMAKE_INSTALL_MESSAGE=" + - conf().cmake().install_message().value()) + conf_cmake::to_string(conf().cmake().install_message())) .arg("--log-level=ERROR") .arg("--no-warn-unused-cli"); diff --git a/src/tools/cmake.h b/src/tools/cmake.h index 41a50dc..ea8a3a6 100644 --- a/src/tools/cmake.h +++ b/src/tools/cmake.h @@ -14,23 +14,25 @@ namespace mob { // type of build files generated // - enum generators { + enum class generators { // generates build files for visual studio vs = 0x01, // generates build files for jom/nmake jom = 0x02 }; + using enum generators; // what run() will do // - enum ops { + enum class ops { // generates the build files generate = 1, // cleans the build files so they're regenerated from scratch clean }; + using enum ops; cmake(ops o = generate); diff --git a/src/tools/msbuild.cpp b/src/tools/msbuild.cpp index c7accd1..8eb8b7f 100644 --- a/src/tools/msbuild.cpp +++ b/src/tools/msbuild.cpp @@ -7,8 +7,8 @@ namespace mob { msbuild::msbuild(ops o) - : basic_process_runner("msbuild"), op_(o), config_("Release"), arch_(arch::def), - flags_(noflags) + : basic_process_runner("msbuild"), op_(o), config_(config::release), + arch_(arch::def), flags_(noflags) { } @@ -17,6 +17,20 @@ namespace mob { return conf().tool().get("msbuild"); } + std::string msbuild::configuration_name(config c) + { + switch (c) { + case config::debug: + return "Debug"; + case config::release: + return "Release"; + case config::relwithdebinfo: + [[fallthrough]]; + default: + return "RelWithDebInfo"; + } + } + msbuild& msbuild::solution(const fs::path& sln) { sln_ = sln; @@ -35,9 +49,9 @@ namespace mob { return *this; } - msbuild& msbuild::config(const std::string& s) + msbuild& msbuild::configuration(config c) { - config_ = s; + config_ = c; return *this; } @@ -151,7 +165,7 @@ namespace mob { .arg("-property:EnforceProcessCountAcrossBuilds=true"); } - p.arg("-property:Configuration=", config_, process::quote) + p.arg("-property:Configuration=", configuration_name(config_), process::quote) .arg("-property:PlatformToolset=" + toolset) .arg("-property:WindowsTargetPlatformVersion=" + vs::sdk()) .arg("-property:Platform=", platform_property(), process::quote) diff --git a/src/tools/msbuild.h b/src/tools/msbuild.h index e70aeff..7cde18f 100644 --- a/src/tools/msbuild.h +++ b/src/tools/msbuild.h @@ -1,6 +1,8 @@ #pragma once +#include "../core/conf.h" #include "../core/env.h" +#include "cmake.h" namespace mob { @@ -14,11 +16,17 @@ namespace mob { // static fs::path binary(); - enum flags_t { noflags = 0x00, single_job = 0x01, allow_failure = 0x02 }; + // retrieve the name of the configuration for the given config + // + static std::string configuration_name(config c); + + enum class flags_t { noflags = 0x00, single_job = 0x01, allow_failure = 0x02 }; + using enum flags_t; // what run() should do // - enum ops { build = 1, clean }; + enum class ops { build = 1, clean }; + using enum ops; msbuild(ops o = build); @@ -36,7 +44,7 @@ namespace mob { // sets "-property:Configuration=s" // - msbuild& config(const std::string& s); + msbuild& configuration(config config); // sets "-property:Platform=s"; if not set, uses architecture() to figure // it out @@ -69,7 +77,7 @@ namespace mob { fs::path sln_; std::vector targets_; std::vector props_; - std::string config_; + config config_; std::string platform_; arch arch_; flags_t flags_; diff --git a/src/utility.h b/src/utility.h index 1a73a29..a0becf4 100644 --- a/src/utility.h +++ b/src/utility.h @@ -46,6 +46,8 @@ namespace mob { def = x64 }; + enum class config { debug, relwithdebinfo, release }; + class url; // returns a url for a prebuilt binary having the given filename; prebuilts are