Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Adapt root prefix determination #3782

Merged
merged 15 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Comment on lines +1 to +6
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do people think of this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any strong opinion about this. We can still revert it later if people are annoyed by the warning.

__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
Loading