From 195112794aebfd6f1b94582af1259488a13ff348 Mon Sep 17 00:00:00 2001 From: Julien Jerphanion Date: Thu, 30 Jan 2025 10:50:04 +0200 Subject: [PATCH] fix: Adapt root prefix determination (#3782) Signed-off-by: Julien Jerphanion Co-authored-by: Klaim --- libmamba/include/mamba/core/util_os.hpp | 1 + libmamba/src/api/configuration.cpp | 108 +++++++++++++++--------- libmamba/src/core/util_os.cpp | 58 ++++++++++++- libmamba/src/download/downloader.cpp | 2 - micromamba/etc/profile.d/mamba.sh.in | 8 +- 5 files changed, 133 insertions(+), 44 deletions(-) diff --git a/libmamba/include/mamba/core/util_os.hpp b/libmamba/include/mamba/core/util_os.hpp index 394b6ac4b6..8be943d5e0 100644 --- a/libmamba/include/mamba/core/util_os.hpp +++ b/libmamba/include/mamba/core/util_os.hpp @@ -22,6 +22,7 @@ namespace mamba bool is_admin(); fs::u8path get_self_exe_path(); + fs::u8path get_libmamba_path(); using PID = #ifdef _WIN32 diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index e7fd2e5400..d368e883c6 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -635,7 +635,7 @@ namespace mamba if (mamba_bin_path.empty()) { return make_unexpected( - "`mamba` binary not found.\nPlease set `MAMBA_ROOT_PREFIX`.", + "The root prefix of your installation cannot be found.\nPlease set `MAMBA_ROOT_PREFIX`.", mamba_error_code::incorrect_usage ); } @@ -653,9 +653,9 @@ namespace mamba return make_unexpected("Empty root prefix.", mamba_error_code::incorrect_usage); } - if (!fs::exists(prefix / "pkgs") // - && !fs::exists(prefix / "conda-meta") // - && !fs::exists(prefix / "envs")) + // TODO: consider the conjunction (i.e. &&-chaining) of the following conditions. + auto qualifies_as_root_prefix = (fs::exists(prefix / "pkgs") || fs::exists(prefix / "conda-meta") || fs::exists(prefix / "envs")); + if (!qualifies_as_root_prefix) { return make_unexpected( fmt::format( @@ -713,51 +713,79 @@ namespace mamba return { fs::weakly_canonical(std::move(prefix)) }; } - auto get_default_root_prefix(fs::u8path& prefix) -> void + auto get_root_prefix() -> fs::u8path { - if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX")) + fs::u8path root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or(""); + + if (!root_prefix.empty()) + { + LOG_TRACE << "Using root prefix set in `MAMBA_ROOT_PREFIX`: " << root_prefix; + return root_prefix; + } + + root_prefix = util::get_env("MAMBA_DEFAULT_ROOT_PREFIX").value_or(""); + + if (!root_prefix.empty()) { - prefix = util::get_env("MAMBA_DEFAULT_ROOT_PREFIX").value(); LOG_WARNING << unindent(R"( - 'MAMBA_DEFAULT_ROOT_PREFIX' is meant for testing purpose. - Consider using 'MAMBA_ROOT_PREFIX' instead)"); + 'MAMBA_DEFAULT_ROOT_PREFIX' is meant for testing purpose. + Consider using 'MAMBA_ROOT_PREFIX' instead)"); + LOG_TRACE << "Using root prefix set in `MAMBA_DEFAULT_ROOT_PREFIX`: " << root_prefix; + return root_prefix; } - else + + // Find the location of libmamba + const fs::u8path libmamba_path = get_libmamba_path(); + + // Find the environment directory of the executable + const fs::u8path env_prefix = fs::weakly_canonical( + libmamba_path.parent_path().parent_path() + ); + + if (auto maybe_prefix = validate_existing_root_prefix(env_prefix); + maybe_prefix.has_value()) { -#ifdef MAMBA_USE_INSTALL_PREFIX_AS_BASE - // mamba case - // set the root prefix as the mamba installation path - get_root_prefix_from_mamba_bin(util::which("mamba")) - .transform([&](fs::u8path&& p) { prefix = std::move(p); }) - .or_else([](mamba_error&& error) { throw std::move(error); }); -#else - // micromamba case - - // In 1.0, only micromamba was using this location. - const fs::u8path default_root_prefix_v1 = fs::u8path(util::user_home_dir()) - / "micromamba"; - - // In 2.0, we change the default location. - // We unconditionally name the subfolder "mamba" for compatibility between ``mamba`` - // and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables. - const fs::u8path default_root_prefix_v2 = fs::u8path(util::user_data_dir()) / "mamba"; - - validate_existing_root_prefix(default_root_prefix_v1) - .or_else([&default_root_prefix_v2](const auto& /* error */) - { return validate_root_prefix(default_root_prefix_v2); }) - .transform([&](fs::u8path&& p) { prefix = std::move(p); }) - .or_else([](mamba_error&& error) { throw std::move(error); }); -#endif + LOG_TRACE << "Using `libmamba`'s current environment as the root prefix: " + << maybe_prefix.value(); + return maybe_prefix.value(); } - } - auto get_root_prefix() -> fs::u8path - { - fs::u8path root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or(""); - if (root_prefix.empty()) + // From the environment directory, we might infer the root prefix. + const fs::u8path inferred_root_prefix = fs::weakly_canonical( + env_prefix.parent_path().parent_path() + ); + + if (auto maybe_prefix = validate_existing_root_prefix(env_prefix); + maybe_prefix.has_value()) { - get_default_root_prefix(root_prefix); + LOG_TRACE << "Inferring and using the root prefix from `libmamba`'s current environment' as: " + << maybe_prefix.value(); + return maybe_prefix.value(); } + +#ifdef MAMBA_USE_INSTALL_PREFIX_AS_BASE + // mamba case + // set the root prefix as the mamba installation path + get_root_prefix_from_mamba_bin(util::which("mamba")) + .transform([&](fs::u8path&& p) { root_prefix = std::move(p); }) + .or_else([](mamba_error&& error) { throw std::move(error); }); +#else + // micromamba case + // In 1.0, only micromamba was using this location. + const fs::u8path default_root_prefix_v1 = fs::u8path(util::user_home_dir()) + / "micromamba"; + + // In 2.0, we change the default location. + // We unconditionally name the subfolder "mamba" for compatibility between ``mamba`` + // and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables. + const fs::u8path default_root_prefix_v2 = fs::u8path(util::user_data_dir()) / "mamba"; + + validate_existing_root_prefix(default_root_prefix_v1) + .or_else([&default_root_prefix_v2](const auto& /* error */) + { return validate_root_prefix(default_root_prefix_v2); }) + .transform([&](fs::u8path&& p) { root_prefix = std::move(p); }) + .or_else([](mamba_error&& error) { throw std::move(error); }); +#endif return root_prefix; } diff --git a/libmamba/src/core/util_os.cpp b/libmamba/src/core/util_os.cpp index 6ed3357cbb..4332dacc09 100644 --- a/libmamba/src/core/util_os.cpp +++ b/libmamba/src/core/util_os.cpp @@ -3,7 +3,8 @@ #ifndef _WIN32 #include - +// To find the path of `libmamba`'s library. +#include #include #include #include @@ -32,6 +33,7 @@ #include #include +#include "mamba/core/error_handling.hpp" #include "mamba/core/output.hpp" #include "mamba/core/util_os.hpp" #include "mamba/util/build.hpp" @@ -89,6 +91,60 @@ namespace mamba #endif } + fs::u8path get_libmamba_path() + { +#ifdef _WIN32 + HMODULE hModule = NULL; + BOOL ret_code = GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR) get_libmamba_path, + &hModule + ); + if (!ret_code) + { + throw mamba::mamba_error( + "Could find libmamba's module handle. (GetModuleHandleExW failed)", + mamba_error_code::internal_failure + ); + } + std::wstring buffer(MAX_PATH, '\0'); + DWORD new_size = MAX_PATH; + DWORD size = 0; + while (true) + { + size = GetModuleFileNameW(hModule, buffer.data(), static_cast(buffer.size())); + if (size == 0) + { + throw mamba::mamba_error( + "Could find the filename of the libmamba's module handle. (GetModuleFileNameW failed)", + mamba_error_code::internal_failure + ); + } + if (size < new_size) + { + break; + } + + new_size *= 2; + buffer.resize(new_size); + } + buffer.resize(size); + return fs::absolute(buffer); +#else + fs::u8path libmamba_path; + Dl_info dl_info; + if (!dladdr(reinterpret_cast(get_libmamba_path), &dl_info)) + { + throw mamba_error( + "Could not find libmamba's path. (dladdr failed)", + mamba_error_code::internal_failure + ); + } + libmamba_path = dl_info.dli_fname; + return libmamba_path; +#endif + } + bool is_admin() { #ifdef _WIN32 diff --git a/libmamba/src/download/downloader.cpp b/libmamba/src/download/downloader.cpp index 6f905fe68e..567f90eb7c 100644 --- a/libmamba/src/download/downloader.cpp +++ b/libmamba/src/download/downloader.cpp @@ -86,8 +86,6 @@ namespace mamba::download { // Use the CA certificates from `conda-forge::ca-certificates` installed in the // root prefix or the system CA certificates if the certificate is not present. - fs::u8path libmamba_library_path; - fs::u8path root_prefix = detail::get_root_prefix(); fs::u8path env_prefix_conda_cert = root_prefix / "ssl" / "cacert.pem"; diff --git a/micromamba/etc/profile.d/mamba.sh.in b/micromamba/etc/profile.d/mamba.sh.in index 4fdcfde932..f3ee55d68a 100644 --- a/micromamba/etc/profile.d/mamba.sh.in +++ b/micromamba/etc/profile.d/mamba.sh.in @@ -1,4 +1,10 @@ -export MAMBA_ROOT_PREFIX="@CMAKE_INSTALL_PREFIX@" +if [ -z "${MAMBA_ROOT_PREFIX}" ]; then + echo "WARNING: MAMBA_ROOT_PREFIX is not set." + echo "WARNING: Please set `MAMBA_ROOT_PREFIX` to the root of your installation." + echo "WARNING: For now continuing with `MAMBA_ROOT_PREFIX` set to `@CMAKE_INSTALL_PREFIX@`." + export MAMBA_ROOT_PREFIX="@CMAKE_INSTALL_PREFIX@" +fi + __mamba_setup="$("@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/mamba" shell hook --shell posix 2> /dev/null)" if [ $? -eq 0 ]; then eval "$__mamba_setup"