From bd6a03d2f8c5df9cd1764ba76990e7bd80b76539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 22 May 2025 19:02:43 +0200 Subject: [PATCH] Add VCPKG support for USVFS. (#149) --- bootstrap.ps1 | 2 +- mob.ini | 1 + src/core/conf.cpp | 3 +- src/core/conf.h | 1 + src/core/paths.cpp | 18 ++++++ src/core/paths.h | 5 ++ src/main.cpp | 2 +- src/tasks/tasks.h | 1 + src/tasks/usvfs.cpp | 46 +++++++-------- src/tools/cmake.cpp | 137 ++++++++++++++++++++++++++++++++++++-------- src/tools/cmake.h | 30 ++++++++++ 11 files changed, 195 insertions(+), 51 deletions(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 98decdc..f4d6e1b 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -13,7 +13,7 @@ if (!$root) { $output = if ($Verbose) { "Out-Default" } else { "Out-Null" } -cmake -B $root/build $root | & $output +cmake -B $root/build -G "Visual Studio 17 2022" $root | & $output $installationPath = & $root\third-party\bin\vswhere.exe -products * -nologo -prerelease -latest -property installationPath if (! $?) { diff --git a/mob.ini b/mob.ini index 406cb63..d79b962 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/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..0b98d7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -166,7 +166,7 @@ namespace mob { for (auto& c : commands) all_groups.push_back(c->group()); - // vs reports a no-op on the left side of the command, which is incorrect + // vs reports a no-op on the left side of the command, which is incorrect #pragma warning(suppress : 4548) auto cli = (all_groups, command::common_options_group()); auto pr = clipp::parse(args, cli); diff --git a/src/tasks/tasks.h b/src/tasks/tasks.h index b175a98..54afb97 100644 --- a/src/tasks/tasks.h +++ b/src/tasks/tasks.h @@ -672,6 +672,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/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)); + } + + cmake usvfs::create_cmake_tool(arch a, cmake::ops o) const + { + return std::move( + 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 { - // 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())))); + 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"); + .binary(binary()); - 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_ + "\""); + 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()) { - p.env(env::vs(arch_).set("CXXFLAGS", "/wd4566")).cwd(build_path()); + 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_)); + + 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