Skip to content

Commit

Permalink
fix: Adapt root prefix determination (#3782)
Browse files Browse the repository at this point in the history
Signed-off-by: Julien Jerphanion <[email protected]>
Co-authored-by: Klaim <[email protected]>
  • Loading branch information
jjerphan and Klaim authored Jan 30, 2025
1 parent 6a28649 commit 1951127
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 44 deletions.
1 change: 1 addition & 0 deletions libmamba/include/mamba/core/util_os.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace mamba

bool is_admin();
fs::u8path get_self_exe_path();
fs::u8path get_libmamba_path();

using PID =
#ifdef _WIN32
Expand Down
108 changes: 68 additions & 40 deletions libmamba/src/api/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
Expand All @@ -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(
Expand Down Expand Up @@ -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;
}

Expand Down
58 changes: 57 additions & 1 deletion libmamba/src/core/util_os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions libmamba/src/download/downloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
8 changes: 7 additions & 1 deletion micromamba/etc/profile.d/mamba.sh.in
Original file line number Diff line number Diff line change
@@ -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"
Expand Down

0 comments on commit 1951127

Please sign in to comment.