fix: Adapt root prefix determination (#3782)

Signed-off-by: Julien Jerphanion <git@jjerphan.xyz>
Co-authored-by: Klaim <Klaim@users.noreply.github.com>
This commit is contained in:
Julien Jerphanion 2025-01-30 10:50:04 +02:00 committed by GitHub
parent 6a286490ee
commit 195112794a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 137 additions and 48 deletions

View File

@ -22,6 +22,7 @@ namespace mamba
bool is_admin();
fs::u8path get_self_exe_path();
fs::u8path get_libmamba_path();
using PID =
#ifdef _WIN32

View File

@ -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
{
if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX"))
{
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)");
}
else
{
#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
}
}
auto get_root_prefix() -> fs::u8path
{
fs::u8path root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or("");
if (root_prefix.empty())
if (!root_prefix.empty())
{
get_default_root_prefix(root_prefix);
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())
{
LOG_WARNING << unindent(R"(
'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;
}
// 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())
{
LOG_TRACE << "Using `libmamba`'s current environment as the root prefix: "
<< maybe_prefix.value();
return maybe_prefix.value();
}
// 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())
{
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;
}

View File

@ -3,7 +3,8 @@
#ifndef _WIN32
#include <clocale>
// To find the path of `libmamba`'s library.
#include <dlfcn.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <unistd.h>
@ -32,6 +33,7 @@
#include <fmt/ostream.h>
#include <reproc++/run.hpp>
#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<DWORD>(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<void*>(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

View File

@ -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";

View File

@ -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"