From 314fde040b971e92856b0d8107fa647c2200cdc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 8 Jul 2024 19:46:46 +0200 Subject: [PATCH] Move to VCPKG. * Use CMake with presets to generate USVFS solution. * Use preset when building super repository. * Allow overriding VCPKG_ROOT with [paths] entry. * Remove gamebryo and nuget specific stuff. * Remove cmake command. Add done log. * Use VCPKG from VS if no VCPKG_ROOT or path setting. --- mob.ini | 1 + src/cmd/cmake.cpp | 70 ------------------- src/cmd/commands.h | 21 ------ src/core/conf.cpp | 3 +- src/core/conf.h | 1 + src/core/paths.cpp | 18 +++++ src/core/paths.h | 5 ++ src/main.cpp | 88 +++++++++-------------- src/tasks/modorganizer.cpp | 131 +++++++++++++--------------------- src/tasks/task.cpp | 1 + src/tasks/tasks.h | 56 ++------------- src/tasks/translations.cpp | 63 +---------------- src/tasks/usvfs.cpp | 46 ++++++------ src/tools/cmake.cpp | 139 ++++++++++++++++++++++++++++++------- src/tools/cmake.h | 30 ++++++++ 15 files changed, 281 insertions(+), 392 deletions(-) delete mode 100644 src/cmd/cmake.cpp diff --git a/mob.ini b/mob.ini index b32c74d..15ce6b5 100644 --- a/mob.ini +++ b/mob.ini @@ -155,6 +155,7 @@ install_licenses = install_pythoncore = install_translations = vs = +vcpkg = qt_install = qt_bin = qt_translations = diff --git a/src/cmd/cmake.cpp b/src/cmd/cmake.cpp deleted file mode 100644 index f6ef356..0000000 --- a/src/cmd/cmake.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "pch.h" -#include "../tasks/tasks.h" -#include "commands.h" - -namespace mob { - - cmake_command::cmake_command() : command(requires_options) {} - - command::meta_t cmake_command::meta() const - { - return {"cmake", "runs cmake in a directory"}; - } - - clipp::group cmake_command::do_group() - { - return clipp::group( - clipp::command("cmake").set(picked_), - - (clipp::option("-h", "--help") >> help_) % "shows this message", - - (clipp::option("-G", "--generator") & clipp::value("GEN") >> gen_) % - ("sets the -G option for cmake [default: VS]"), - - (clipp::option("-c", "--cmd") & clipp::value("CMD") >> cmd_) % - "overrides the cmake command line [default: \"..\"]", - - (clipp::option("--x64").set(x64_, true) | - clipp::option("--x86").set(x64_, false)) % - "whether to use the x64 or x86 vcvars; if -G is not set, " - "whether to pass \"-A Win32\" or \"-A x64\" for the default " - "VS generator [default: x64]", - - (clipp::option("--install-prefix") & clipp::value("PATH") >> prefix_) % - "sets CMAKE_INSTALL_PREFIX [default: empty]", - - (clipp::option("-d", "--debug").set(debug_, true)) % - "whether to configure for debug mode [default: false]", - - (clipp::value("PATH") >> path_) % "path from which to run `cmake`"); - } - - int cmake_command::do_run() - { - auto t = tasks::modorganizer::create_cmake_tool( - fs::path(utf8_to_utf16(path_)), mob::cmake::generate, - debug_ ? config::debug : config::relwithdebinfo); - - t.generator(gen_); - t.cmd(cmd_); - t.prefix(prefix_); - t.output(path_); - - if (!x64_) - t.architecture(arch::x86); - - // copy the global context, the tool will modify it - context cxcopy(gcx()); - - t.run(cxcopy); - - return 0; - } - - std::string cmake_command::do_doc() - { - return "Runs `cmake ..` in the given directory with the same command line\n" - "as the one used for modorganizer projects."; - } - -} // namespace mob diff --git a/src/cmd/commands.h b/src/cmd/commands.h index 267a133..beb177c 100644 --- a/src/cmd/commands.h +++ b/src/cmd/commands.h @@ -367,27 +367,6 @@ namespace mob { std::vector get_repos() const; }; - // runs cmake in a directory with the same parameters as `build` would - // - class cmake_command : public command { - public: - cmake_command(); - meta_t meta() const override; - - protected: - clipp::group do_group() override; - int do_run() override; - std::string do_doc() override; - - private: - std::string gen_; - std::string cmd_; - bool x64_ = true; - bool debug_ = false; - std::string prefix_; - std::string path_; - }; - // lists the inis found by mob // class inis_command : public command { diff --git a/src/core/conf.cpp b/src/core/conf.cpp index e122f13..82b38f1 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -509,6 +509,7 @@ namespace mob { set_path_if_empty("pf_x86", find_program_files_x86); set_path_if_empty("pf_x64", find_program_files_x64); set_path_if_empty("vs", find_vs); + set_path_if_empty("vcpkg", find_vcpkg); // set after vs as it will use the VS set_path_if_empty("qt_install", find_qt); set_path_if_empty("temp_dir", find_temp_dir); set_path_if_empty("patches", find_in_root("patches")); @@ -530,7 +531,7 @@ namespace mob { resolve_path("install", p.prefix(), "install"); resolve_path("install_installer", p.install(), "installer"); resolve_path("install_bin", p.install(), "bin"); - resolve_path("install_libs", p.install(), "libs"); + resolve_path("install_libs", p.install(), "lib"); resolve_path("install_pdbs", p.install(), "pdb"); resolve_path("install_dlls", p.install_bin(), "dlls"); resolve_path("install_loot", p.install_bin(), "loot"); diff --git a/src/core/conf.h b/src/core/conf.h index 2589394..c1cbcbe 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -264,6 +264,7 @@ namespace mob { VALUE(install_translations); VALUE(vs); + VALUE(vcpkg); VALUE(qt_install); VALUE(qt_bin); VALUE(qt_translations); diff --git a/src/core/paths.cpp b/src/core/paths.cpp index 8652459..9248a64 100644 --- a/src/core/paths.cpp +++ b/src/core/paths.cpp @@ -254,6 +254,24 @@ namespace mob { } } + fs::path find_vcpkg() + { + const auto env_path = this_env::get().get("VCPKG_ROOT"); + + if (!env_path.empty()) { + return fs::absolute(env_path); + } + + const auto vs_path = conf().path().vs(); + const auto vcpkg_vs_path = vs_path / "VC" / "vcpkg"; + if (!exists(vcpkg_vs_path)) { + gcx().bail_out(context::conf, "vcpkg is not part of VS installation at {}", + vs_path); + } + + return vcpkg_vs_path; + } + fs::path find_qt() { // check from the ini first diff --git a/src/core/paths.h b/src/core/paths.h index 597823a..b36d806 100644 --- a/src/core/paths.h +++ b/src/core/paths.h @@ -33,6 +33,11 @@ namespace mob { // fs::path find_vs(); + // returns the absolute path to VCPKG root directory to be used as VCPKG_ROOT when + // building + // + fs::path find_vcpkg(); + // returns the absolute path to Qt's root directory, the one that contains // bin, include, etc.; bails if not found // diff --git a/src/main.cpp b/src/main.cpp index ce35867..61d274c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,33 +26,33 @@ namespace mob { // third-party tasks - add_task() - .add_task() - .add_task() - .add_task() - .add_task() - .add_task() - .add_task() - .add_task() - .add_task(); - - add_task() - .add_task() - .add_task() - .add_task(); - - add_task() - .add_task() - .add_task() - .add_task(); - - add_task() - .add_task() - .add_task() - .add_task() - .add_task() - .add_task() - .add_task(); + // add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task(); + + // add_task() + // .add_task() + // .add_task() + // .add_task(); + + // add_task() + // .add_task() + // .add_task() + // .add_task(); + + // add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task() + // .add_task(); // super tasks @@ -61,9 +61,9 @@ namespace mob { // most of the alternate names below are from the transifex slugs, which // are sometimes different from the project names, for whatever reason - add_task() - .add_task("cmake_common") - .add_task("modorganizer-uibase"); + add_task().add_task().add_task("cmake_common"); + + add_task("modorganizer-uibase"); add_task() .add_task("modorganizer-archive") @@ -72,30 +72,9 @@ namespace mob { .add_task("modorganizer-bsatk") .add_task("modorganizer-nxmhandler") .add_task("modorganizer-helper") - .add_task("githubpp") - .add_task("modorganizer-game_gamebryo") .add_task({"modorganizer-bsapacker", "bsa_packer"}) - .add_task("modorganizer-preview_bsa"); - - // the gamebryo flag must be set for all game plugins that inherit from - // the gamebryo classes; this will merge the .ts file from gamebryo with - // the one from the specific plugin - add_task() - .add_task("modorganizer-game_oblivion", mo::gamebryo) - .add_task("modorganizer-game_nehrim", mo::gamebryo) - .add_task("modorganizer-game_fallout3", mo::gamebryo) - .add_task("modorganizer-game_fallout4", mo::gamebryo) - .add_task("modorganizer-game_fallout4vr", mo::gamebryo) - .add_task("modorganizer-game_fallout76", mo::gamebryo) - .add_task("modorganizer-game_falloutnv", mo::gamebryo) - .add_task("modorganizer-game_morrowind", mo::gamebryo) - .add_task("modorganizer-game_skyrim", mo::gamebryo) - .add_task("modorganizer-game_skyrimse", mo::gamebryo) - .add_task("modorganizer-game_skyrimvr", mo::gamebryo) - .add_task("modorganizer-game_starfield", mo::gamebryo) - .add_task("modorganizer-game_ttw", mo::gamebryo) - .add_task("modorganizer-game_enderal", mo::gamebryo) - .add_task("modorganizer-game_enderalse", mo::gamebryo); + .add_task("modorganizer-preview_bsa") + .add_task("modorganizer-game_bethesda"); add_task() .add_task({"modorganizer-tool_inieditor", "inieditor"}) @@ -109,7 +88,7 @@ namespace mob { .add_task("modorganizer-installer_quick") .add_task("modorganizer-installer_fomod") .add_task("modorganizer-installer_fomod_csharp") - .add_task("modorganizer-installer_omod", mo::nuget) + .add_task("modorganizer-installer_omod") .add_task("modorganizer-installer_wizard") .add_task("modorganizer-bsa_extractor") .add_task("modorganizer-plugin_python"); @@ -147,7 +126,6 @@ namespace mob { std::make_unique(), std::make_unique(), std::make_unique(), - std::make_unique(), std::make_unique(), std::make_unique()}; diff --git a/src/tasks/modorganizer.cpp b/src/tasks/modorganizer.cpp index 2ad8f9d..9a053d1 100644 --- a/src/tasks/modorganizer.cpp +++ b/src/tasks/modorganizer.cpp @@ -3,6 +3,15 @@ namespace mob::tasks { + // build CMAKE_PREFIX_PATH for MO2 tasks + // + std::string cmake_prefix_path() + { + return conf().path().qt_install().string() + ";" + + (modorganizer::super_path() / "cmake_common").string() + ";" + + (conf().path().install() / "lib" / "cmake").string(); + } + // given a vector of names (some projects have more than one, see add_tasks() in // main.cpp), this prepends the simplified name to the vector and returns it // @@ -59,18 +68,18 @@ namespace mob::tasks { g.init_repo(); } - modorganizer::modorganizer(std::string long_name, flags f) - : modorganizer(std::vector{long_name}, f) + modorganizer::modorganizer(std::string long_name) + : modorganizer(std::vector{long_name}) { } - modorganizer::modorganizer(std::vector names, flags f) - : modorganizer(std::vector(names.begin(), names.end()), f) + modorganizer::modorganizer(std::vector names) + : modorganizer(std::vector(names.begin(), names.end())) { } - modorganizer::modorganizer(std::vector names, flags f) - : task(make_names(names)), repo_(names[0]), flags_(f) + modorganizer::modorganizer(std::vector names) + : task(make_names(names)), repo_(names[0]) { if (names.size() > 1) { project_ = names[1]; @@ -80,34 +89,15 @@ namespace mob::tasks { } } - bool modorganizer::is_gamebryo_plugin() const - { - return is_set(flags_, gamebryo); - } - - bool modorganizer::is_nuget_plugin() const - { - return is_set(flags_, nuget); - } - fs::path modorganizer::source_path() const { // something like build/modorganizer_super/uibase return super_path() / name(); } - fs::path modorganizer::project_file_path() const - { - // ask cmake for the build path it would use - const auto build_path = create_cmake_tool(source_path()).build_path(); - - // use the INSTALL project - return build_path / (project_ + ".sln"); - } - fs::path modorganizer::super_path() { - return conf().path().build() / "modorganizer_super"; + return conf().path().build(); } url modorganizer::git_url() const @@ -137,11 +127,7 @@ namespace mob::tasks { // cmake clean if (is_set(c, clean::reconfigure)) - run_tool(create_cmake_tool(cmake::clean)); - - // msbuild clean - if (is_set(c, clean::rebuild)) - run_tool(create_msbuild_tool(msbuild::clean)); + run_tool(cmake(cmake::clean).root(source_path())); } void modorganizer::do_fetch() @@ -165,7 +151,7 @@ namespace mob::tasks { void modorganizer::do_build_and_install() { - // adds a git submodule in modorganizer_super for this project; note that + // adds a git submodule in build for this project; note that // git_submodule_adder runs a thread because adding submodules is slow, but // can happen while stuff is building git_submodule_adder::instance().queue( @@ -177,65 +163,42 @@ namespace mob::tasks { // not all modorganizer projects need to actually be built, such as // cmake_common, so don't try if there's no cmake file - if (!fs::exists(source_path() / "CMakeLists.txt")) { + if (!exists(source_path() / "CMakeLists.txt")) { cx().trace(context::generic, "{} has no CMakeLists.txt, not building", repo_); return; } - // run cmake - run_tool(create_cmake_tool()); - - // run restore for nuget - // - // until https://gitlab.kitware.com/cmake/cmake/-/issues/20646 is resolved, - // we need a manual way of running the msbuild -t:restore - if (is_nuget_plugin()) - run_tool(create_msbuild_tool().targets({"restore"})); - - // run msbuild - run_tool(create_msbuild_tool()); - } - - cmake modorganizer::create_cmake_tool(cmake::ops o) - { - return create_cmake_tool(source_path(), o, task_conf().configuration()); - } - - 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()) - // 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()) - .def("DIRECTXTEX_ROOT", directxtex::source_path()) - .root(root)); - } + // if there is a CMakeLists.txt, there must be a CMakePresets.json otherwise + // we cannot build + if (!exists(source_path() / "CMakePresets.json")) { + gcx().bail_out(context::generic, + "{} has no CMakePresets.txt, aborting build", repo_); + } - msbuild modorganizer::create_msbuild_tool(msbuild::ops o) - { - return std::move(msbuild(o) - .solution(project_file_path()) - .configuration(task_conf().configuration()) - .architecture(arch::x64)); + // run cmake + run_tool(cmake(cmake::generate) + .generator(cmake::vs) + .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) + .def("CMAKE_PREFIX_PATH", cmake_prefix_path()) + .preset("vs2022-windows") + .root(source_path())); + + // run cmake --build with default target + // TODO: handle rebuild by adding `--clean-first` + // TODO: have a way to specify the `--parallel` value - 16 is useful to build + // game_bethesda that has 15 games, so 15 projects + run_tool(cmake(cmake::build) + .root(source_path()) + .arg("--parallel") + .arg("16") + .configuration(mob::config::relwithdebinfo)); + + // run cmake --install + run_tool(cmake(cmake::install) + .root(source_path()) + .configuration(mob::config::relwithdebinfo)); } } // namespace mob::tasks diff --git a/src/tasks/task.cpp b/src/tasks/task.cpp index 451366b..5f411ee 100644 --- a/src/tasks/task.cpp +++ b/src/tasks/task.cpp @@ -413,6 +413,7 @@ namespace mob { cx().info(context::generic, "build and install"); do_build_and_install(); + cx().info(context::generic, "done"); } void task::check_bailed() diff --git a/src/tasks/tasks.h b/src/tasks/tasks.h index 8b9b116..390258c 100644 --- a/src/tasks/tasks.h +++ b/src/tasks/tasks.h @@ -216,34 +216,13 @@ namespace mob::tasks { cmake::ops o = cmake::generate, config config = config::relwithdebinfo); - // flags for some MO projects - enum flags { - noflags = 0x00, - - // gamebryo project, used by the translations task because these - // projects have multiple .ts files that have to be merged - gamebryo = 0x01, - - // project that uses nuget, cmake doesn't support those right now, so - // `msbuild -t:restore` has to be run manually - nuget = 0x02, - }; - // some mo tasks have more than one name, mostly because the transifex slugs // are different than the names on github; the std::string and const char* // overloads are because they're constructed from initializer lists and it's // more convenient that way - modorganizer(std::string name, flags f = noflags); - modorganizer(std::vector names, flags f = noflags); - modorganizer(std::vector names, flags f = noflags); - - // whether this project has the gamebryo flag on - // - bool is_gamebryo_plugin() const; - - // whether this project has the nuget flag on - // - bool is_nuget_plugin() const; + modorganizer(std::string name); + modorganizer(std::vector names); + modorganizer(std::vector names); // url to the git repo // @@ -271,27 +250,6 @@ namespace mob::tasks { private: std::string repo_; std::string project_; - flags flags_; - - // creates the cmake tool for this MO project - // - cmake create_cmake_tool(cmake::ops o = cmake::generate); - - // creates the msbuild tool for this MO project - // - msbuild create_msbuild_tool(msbuild::ops o = msbuild::build); - - // this is the file targeted by the msbuild tool - // - // it's not actually the .sln file because the cmake files have historically - // been inconsistent in what the main project in the solution is and whether - // the INSTALL project was enabled or not, so just building the .sln itself - // might not actually build everything - // - // by targeting the INSTALL project directly, everything will always be - // built correctly, regardless of how the solution file is generated - // - fs::path project_file_path() const; }; class ncc : public basic_task { @@ -623,11 +581,6 @@ namespace mob::tasks { // duplicate warnings std::set warned_; - // whether the given project name is a gamebryo task, `dir` is just for - // logging - // - bool is_gamebryo_plugin(const std::string& dir, const std::string& project); - // parses the directory name, walks all the .ts files, returns a project // object for them // @@ -636,7 +589,7 @@ namespace mob::tasks { // returns a lang object that contains at least the given main_ts_file, // but might contain more if it's a gamebryo plugin // - lang create_lang(bool gamebryo, const std::string& project_name, + lang create_lang(const std::string& project_name, const fs::path& main_ts_file); }; @@ -671,6 +624,7 @@ namespace mob::tasks { void fetch_from_source(); void build_and_install_from_source(); + cmake create_cmake_tool(arch, cmake::ops = cmake::generate) const; msbuild create_msbuild_tool(arch, msbuild::ops = msbuild::build, config = config::release) const; }; diff --git a/src/tasks/translations.cpp b/src/tasks/translations.cpp index bc29c50..6910b19 100644 --- a/src/tasks/translations.cpp +++ b/src/tasks/translations.cpp @@ -125,9 +125,6 @@ namespace mob::tasks { // walks all the .ts files in the project, creates a `lang` object for // each // - // each project directory is named "mod-organizer-2.project_name", so this - // splits on the dot to get the project name, checks if it's a gamebryo - // plugin, and adds the gamebryo .ts file as well if necessary // splitting const auto dir_name = path_to_utf8(dir.filename()); @@ -150,7 +147,6 @@ namespace mob::tasks { // project project p(project_name); - const bool gamebryo = is_gamebryo_plugin(dir_name, project_name); // for each file for (auto f : fs::directory_iterator(dir)) { @@ -168,14 +164,14 @@ namespace mob::tasks { } // add a new `lang` object for it - p.langs.push_back(create_lang(gamebryo, project_name, f.path())); + p.langs.push_back(create_lang(project_name, f.path())); } return p; } translations::projects::lang - translations::projects::create_lang(bool gamebryo, const std::string& project_name, + translations::projects::create_lang(const std::string& project_name, const fs::path& main_ts_file) { lang lg(path_to_utf8(main_ts_file.stem())); @@ -183,64 +179,9 @@ namespace mob::tasks { // every lang has the .ts file from the project, gamebryo plugins have more lg.ts_files.push_back(main_ts_file); - if (gamebryo) { - // this is a gamebryo plugin, so it needs the gamebryo .ts file as well, - // find it - - // the .ts files for gamebryo are in mod-organizer-2.game_gamebryo/ - const fs::path gamebryo_dir = - conf().transifex().get("project") + "." + "game_gamebryo"; - - // the .ts file has the same name, it's just "lang.ts" - const auto gamebryo_ts = root_ / gamebryo_dir / main_ts_file.filename(); - - if (fs::exists(gamebryo_ts)) { - // found, add it - lg.ts_files.push_back(gamebryo_ts); - } - else { - // not found, that means the plugin was translated into a language, - // but the gamebryo project wasn't; warn once - if (!warned_.contains(gamebryo_ts)) { - warned_.insert(gamebryo_ts); - - warnings_.push_back(::std::format( - "{} is a gamebryo plugin but there is no '{}'; the " - ".qm file will be missing some translations (will " - "only warn once)", - project_name, path_to_utf8(gamebryo_ts))); - } - } - } - return lg; } - bool translations::projects::is_gamebryo_plugin(const std::string& dir, - const std::string& project) - { - const auto* t = task_manager::instance().find_one(project); - - if (!t) { - warnings_.push_back( - ::std::format("directory '{}' was parsed as project '{}', but there's " - "no task with this name", - dir, project)); - - return false; - } - - // gamebryo plugins are all `modorganizer` tasks - const auto* mo_task = static_cast(t); - if (!mo_task) { - // not an mo task, can't be a gamebryo plugin - return false; - } - - // check the flag - return mo_task->is_gamebryo_plugin(); - } - translations::translations() : task("translations") {} fs::path translations::source_path() diff --git a/src/tasks/usvfs.cpp b/src/tasks/usvfs.cpp index a61e277..e2bfe3d 100644 --- a/src/tasks/usvfs.cpp +++ b/src/tasks/usvfs.cpp @@ -40,6 +40,11 @@ namespace mob::tasks { return; } + if (is_set(c, clean::reconfigure)) { + run_tool(create_cmake_tool(arch::x64)); + run_tool(create_cmake_tool(arch::x86)); + } + if (is_set(c, clean::rebuild)) { // msbuild clean run_tool(create_msbuild_tool(arch::x86, msbuild::clean, @@ -69,35 +74,28 @@ namespace mob::tasks { void usvfs::build_and_install_from_source() { - run_tool(create_msbuild_tool(arch::x86)); + run_tool(create_cmake_tool(arch::x64)); + run_tool(create_cmake_tool(arch::x86)); run_tool(create_msbuild_tool(arch::x64)); + run_tool(create_msbuild_tool(arch::x86)); } - msbuild usvfs::create_msbuild_tool(arch a, msbuild::ops o, config c) const + cmake usvfs::create_cmake_tool(arch a, cmake::ops o) const { - // usvfs doesn't use "Win32" for 32-bit, it uses "x86" - // - // note that usvfs_proxy has a custom build step in Release that runs - // usvfs/vsbuild/stage_helper.cmd, which copies everything into - // install/ - - const std::string plat = (a == arch::x64 ? "x64" : "x86"); - - // udis requires python in its custom build step, so make sure it's on the - // path - // - // BOOST_PATH is in the env because external_dependencies.props will - // use it if it exists or reverts to a hardcoded path which might not be using - // the same version as mob if it wasn't updated return std::move( - msbuild(o) - .platform(plat) - .configuration(c) - .targets({"usvfs_proxy"}) - .solution(source_path() / "vsbuild" / "usvfs.sln") - .env(env::vs(a) - .prepend_path(python::build_path()) - .set("BOOST_PATH", path_to_utf8(boost::source_path())))); + cmake(o) + .root(source_path()) + .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) + .generator(cmake::vs) + .preset(a == arch::x64 ? "vs2022-windows-x64" : "vs2022-windows-x86") + .arg("-DBUILD_TESTING=OFF")); + } + + msbuild usvfs::create_msbuild_tool(arch a, msbuild::ops o, config c) const + { + const std::string vsbuild = (a == arch::x64 ? "vsbuild64" : "vsbuild32"); + return std::move(msbuild(o).architecture(a).configuration(c).solution( + source_path() / vsbuild / "usvfs.sln")); } } // namespace mob::tasks diff --git a/src/tools/cmake.cpp b/src/tools/cmake.cpp index 0f277e0..3d8e629 100644 --- a/src/tools/cmake.cpp +++ b/src/tools/cmake.cpp @@ -4,8 +4,23 @@ namespace mob { + namespace { + std::string config_to_string(config c) + { + switch (c) { + case config::debug: + return "Debug"; + case config::release: + return "Release"; + case config::relwithdebinfo: + return "RelWithDebInfo"; + } + gcx().bail_out(context::generic, "unknow configuration type {}", c); + } + } // namespace + cmake::cmake(ops o) - : basic_process_runner("cmake"), op_(o), gen_(jom), arch_(arch::def) + : basic_process_runner("cmake"), op_(o), gen_(vs), arch_(arch::def) { } @@ -62,6 +77,12 @@ namespace mob { return *this; } + cmake& cmake::preset(const std::string& s) + { + preset_ = s; + return *this; + } + cmake& cmake::arg(std::string s) { std::replace(s.begin(), s.end(), '\\', '/'); @@ -75,6 +96,24 @@ namespace mob { return *this; } + cmake& cmake::targets(const std::string& target) + { + targets_ = {target}; + return *this; + } + + cmake& cmake::targets(const std::vector& targets) + { + targets_ = targets; + return *this; + } + + cmake& cmake::configuration(mob::config config) + { + config_ = config; + return *this; + } + cmake& cmake::cmd(const std::string& s) { cmd_ = s; @@ -110,6 +149,16 @@ namespace mob { break; } + case build: { + do_build(); + break; + } + + case install: { + do_install(); + break; + } + default: { cx().bail_out(context::generic, "bad cmake op {}", op_); } @@ -126,43 +175,83 @@ namespace mob { auto p = process() .stdout_encoding(encodings::utf8) .stderr_encoding(encodings::utf8) - .binary(binary()) - .arg("-DCMAKE_BUILD_TYPE=Release") - .arg("-DCMAKE_INSTALL_MESSAGE=" + - conf_cmake::to_string(conf().cmake().install_message())) - .arg("--log-level=ERROR") - .arg("--no-warn-unused-cli"); - - if (genstring_.empty()) { - // there's always a generator name, but some generators don't need - // an architecture flag, like jom, so get_arch() might return an empty - // string - p.arg("-G", "\"" + g.name + "\"") - .arg(g.get_arch(arch_)) - .arg(g.get_host(conf().cmake().host())); - } - else { - // verbatim generator string - p.arg("-G", "\"" + genstring_ + "\""); + .binary(binary()); + + if (!preset_.empty()) { + p = p.arg("--preset").arg(preset_); } + p = p.arg("-DCMAKE_INSTALL_MESSAGE=" + + conf_cmake::to_string(conf().cmake().install_message())) + .arg("--log-level=ERROR") + .arg("--no-warn-unused-cli"); + // prefix if (!prefix_.empty()) p.arg("-DCMAKE_INSTALL_PREFIX=", prefix_); p.args(args_); - // `..` by default, overriden by cmd() - if (cmd_.empty()) - p.arg(".."); - else - p.arg(cmd_); + if (preset_.empty()) { + + if (genstring_.empty()) { + // there's always a generator name, but some generators don't need + // an architecture flag, like jom, so get_arch() might return an empty + // string + p.arg("-G", "\"" + g.name + "\"") + .arg(g.get_arch(arch_)) + .arg(g.get_host(conf().cmake().host())); + } + else { + // verbatim generator string + p.arg("-G", "\"" + genstring_ + "\""); + } + + // `..` by default, overriden by cmd() + if (cmd_.empty()) + p.arg(".."); + else + p.arg(cmd_); + } + + p.env(env::vs(arch_) + .set("CXXFLAGS", "/wd4566") + .set("VCPKG_ROOT", absolute(conf().path().vcpkg()).string())) + .cwd(preset_.empty() ? build_path() : root_); + + execute_and_join(p); + } + + void cmake::do_build() + { + auto p = process() + .stdout_encoding(encodings::utf8) + .stderr_encoding(encodings::utf8) + .binary(binary()) + .arg("--build") + .arg(build_path()) + .arg("--config") + .arg(config_to_string(config_)); - p.env(env::vs(arch_).set("CXXFLAGS", "/wd4566")).cwd(build_path()); + for (auto& target : targets_) { + p = p.arg("--target").arg(target); + } execute_and_join(p); } + void cmake::do_install() + { + execute_and_join(process() + .stdout_encoding(encodings::utf8) + .stderr_encoding(encodings::utf8) + .binary(binary()) + .arg("--install") + .arg(build_path()) + .arg("--config") + .arg(config_to_string(config_))); + } + void cmake::do_clean() { cx().trace(context::rebuild, "deleting all generator directories"); diff --git a/src/tools/cmake.h b/src/tools/cmake.h index ea8a3a6..9420fc5 100644 --- a/src/tools/cmake.h +++ b/src/tools/cmake.h @@ -29,6 +29,12 @@ namespace mob { // generates the build files generate = 1, + // build + build, + + // install + install, + // cleans the build files so they're regenerated from scratch clean }; @@ -54,6 +60,15 @@ namespace mob { // cmake& root(const fs::path& p); + // set the targets for build + // + cmake& targets(const std::string& target); + cmake& targets(const std::vector& target); + + // set the configuration to build or install + // + cmake& configuration(mob::config config); + // overrides the directory in which cmake will write build files // // by default, this is a directory inside what was given in root() with a @@ -75,6 +90,10 @@ namespace mob { cmake& def(const std::string& name, const fs::path& p); cmake& def(const std::string& name, const char* s); + // set a preset to run with cmake --preset + // + cmake& preset(const std::string& s); + // adds an arbitrary argument, passed verbatim // cmake& arg(std::string s); @@ -146,6 +165,9 @@ namespace mob { // what run() does ops op_; + // preset to run + std::string preset_; + // directory where CMakeLists.txt is fs::path root_; @@ -156,6 +178,12 @@ namespace mob { // passed as -DCMAKE_INSTALL_PREFIX fs::path prefix_; + // targets + std::vector targets_; + + // configuration + mob::config config_{mob::config::relwithdebinfo}; + // passed verbatim std::vector args_; @@ -175,6 +203,8 @@ namespace mob { // runs cmake // void do_generate(); + void do_build(); + void do_install(); // returns a list of generators handled by this tool, same ones as in the // `generators` enum on top