mirror of https://github.com/mamba-org/mamba.git
891 lines
31 KiB
C++
891 lines
31 KiB
C++
// Copyright (c) 2019, QuantStack and Mamba Contributors
|
|
//
|
|
// Distributed under the terms of the BSD 3-Clause License.
|
|
//
|
|
// The full license is in the file LICENSE, distributed with this software.
|
|
|
|
#include "activation.hpp"
|
|
#include "environment.hpp"
|
|
|
|
namespace mamba
|
|
{
|
|
namespace
|
|
{
|
|
fs::path PREFIX_STATE_FILE = fs::path("conda-meta") / "state";
|
|
fs::path PACKAGE_ENV_VARS_DIR = fs::path("etc") / "conda" / "env_vars.d";
|
|
std::string CONDA_ENV_VARS_UNSET_VAR = "***unset***";
|
|
}
|
|
|
|
/****************************
|
|
* Activator implementation *
|
|
****************************/
|
|
|
|
Activator::Activator()
|
|
: m_env(env::copy())
|
|
{
|
|
}
|
|
|
|
std::vector<fs::path> Activator::get_activate_scripts(const fs::path& prefix)
|
|
{
|
|
fs::path script_dir = prefix / "etc" / "conda" / "activate.d";
|
|
std::vector<fs::path> result = filter_dir(script_dir, shell_extension());
|
|
std::sort(result.begin(), result.end());
|
|
return result;
|
|
}
|
|
|
|
std::vector<fs::path> Activator::get_deactivate_scripts(const fs::path& prefix)
|
|
{
|
|
fs::path script_dir = prefix / "etc" / "conda" / "deactivate.d";
|
|
std::vector<fs::path> result = filter_dir(script_dir, shell_extension());
|
|
// reverse sort!
|
|
std::sort(result.begin(), result.end(), std::greater<fs::path>());
|
|
return result;
|
|
}
|
|
|
|
std::string Activator::get_default_env(const fs::path& prefix)
|
|
{
|
|
if (paths_equal(prefix, Context::instance().root_prefix))
|
|
{
|
|
return "base";
|
|
}
|
|
// if ../miniconda3/envs/my_super_env, return `my_super_env`, else path
|
|
if (prefix.parent_path().stem() == "envs")
|
|
{
|
|
return prefix.stem();
|
|
}
|
|
else
|
|
{
|
|
return prefix;
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> Activator::get_environment_vars(const fs::path& prefix)
|
|
{
|
|
fs::path env_vars_file = prefix / PREFIX_STATE_FILE;
|
|
fs::path pkg_env_var_dir = prefix / PACKAGE_ENV_VARS_DIR;
|
|
std::vector<std::pair<std::string, std::string>> env_vars;
|
|
|
|
// # First get env vars from packages
|
|
auto env_var_files = filter_dir(pkg_env_var_dir, "");
|
|
std::sort(env_var_files.begin(), env_var_files.end());
|
|
/*for (auto& f : env_var_files)
|
|
{
|
|
// TODO json load env vars and add to map
|
|
// if exists(pkg_env_var_dir):
|
|
// for pkg_env_var_file in sorted(os.listdir(pkg_env_var_dir)):
|
|
// with open(join(pkg_env_var_dir, pkg_env_var_file), 'r') as f:
|
|
// env_vars.update(json.loads(f.read(), object_pairs_hook=OrderedDict))
|
|
}*/
|
|
|
|
// Then get env vars from environment specification
|
|
if (fs::exists(env_vars_file))
|
|
{
|
|
// TODO read this file;
|
|
// with open(env_vars_file, 'r') as f:
|
|
// prefix_state = json.loads(f.read(), object_pairs_hook=OrderedDict)
|
|
// prefix_state_env_vars = prefix_state.get('env_vars', {})
|
|
// dup_vars = [ev for ev in env_vars.keys() if ev in prefix_state_env_vars.keys()]
|
|
// for dup in dup_vars:
|
|
// print("WARNING: duplicate env vars detected. Vars from the environment "
|
|
// "will overwrite those from packages", file=sys.stderr)
|
|
// print("variable %s duplicated" % dup, file=sys.stderr)
|
|
// env_vars.update(prefix_state_env_vars)
|
|
}
|
|
return env_vars;
|
|
}
|
|
|
|
std::string Activator::get_prompt_modifier(const fs::path& prefix,
|
|
const std::string& conda_default_env,
|
|
int old_conda_shlvl)
|
|
{
|
|
if (Context::instance().change_ps1)
|
|
{
|
|
std::vector<std::string> env_stack;
|
|
std::vector<std::string> prompt_stack;
|
|
std::string env_i;
|
|
for (int i = 1; i < old_conda_shlvl + 1; ++i)
|
|
{
|
|
std::string env_prefix = (i == old_conda_shlvl) ? "CONDA_PREFIX" : "CONDA_PREFIX_" + std::to_string(i);
|
|
std::string prefix = (m_env.find(env_prefix) != m_env.end()) ? m_env[env_prefix] : "";
|
|
env_i = get_default_env(prefix);
|
|
|
|
bool stacked_i = m_env.find("CONDA_STACKED_" + std::to_string(i)) != m_env.end();
|
|
env_stack.push_back(env_i);
|
|
|
|
if (!stacked_i && prompt_stack.size() != 0)
|
|
{
|
|
prompt_stack.pop_back();
|
|
}
|
|
prompt_stack.push_back(env_i);
|
|
}
|
|
|
|
// Modify prompt stack according to pending operation
|
|
if (m_action == ActivationType::DEACTIVATE)
|
|
{
|
|
if (prompt_stack.size())
|
|
prompt_stack.pop_back();
|
|
if (env_stack.size())
|
|
env_stack.pop_back();
|
|
bool stacked = m_env.find("CONDA_STACKED_" + std::to_string(old_conda_shlvl)) != m_env.end();
|
|
if (!stacked && env_stack.size())
|
|
{
|
|
prompt_stack.push_back(env_stack.back());
|
|
}
|
|
}
|
|
else if (m_action == ActivationType::REACTIVATE)
|
|
{
|
|
// DO NOTHING
|
|
}
|
|
else
|
|
{
|
|
// stack = getattr(self, 'stack', False)
|
|
if (!m_stack && prompt_stack.size())
|
|
{
|
|
prompt_stack.pop_back();
|
|
}
|
|
prompt_stack.push_back(conda_default_env);
|
|
// TODO there may be more missing here.
|
|
}
|
|
|
|
auto conda_stacked_env = join(";", prompt_stack);
|
|
|
|
std::string prompt = Context::instance().env_prompt;
|
|
replace_all(prompt, "{default_env}", conda_default_env);
|
|
replace_all(prompt, "{stacked_env}", conda_stacked_env);
|
|
replace_all(prompt, "{prefix}", prefix);
|
|
replace_all(prompt, "{name}", prefix.stem());
|
|
return prompt;
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
std::vector<fs::path> Activator::get_path_dirs(const fs::path& prefix)
|
|
{
|
|
if (on_win)
|
|
{
|
|
return {
|
|
prefix / "Library" / "mingw-w64" / "bin",
|
|
prefix / "Library" / "usr" / "bin",
|
|
prefix / "Library" / "bin",
|
|
prefix / "Scripts",
|
|
prefix / "bin"
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return { prefix / "bin" };
|
|
}
|
|
}
|
|
|
|
std::vector<fs::path> Activator::get_clean_dirs()
|
|
{
|
|
// For isolation, running the conda test suite *without* env. var. inheritance
|
|
// every so often is a good idea. We should probably make this a pytest fixture
|
|
// along with one that tests both hardlink-only and copy-only, but before that
|
|
// conda's testsuite needs to be a lot faster!
|
|
// clean_paths = {'darwin': '/usr/bin:/bin:/usr/sbin:/sbin',
|
|
// # You may think 'let us do something more clever here and interpolate
|
|
// # `%windir%`' but the point here is the the whole env. is cleaned out
|
|
// 'win32': 'C:\\Windows\\system32;'
|
|
// 'C:\\Windows;'
|
|
// 'C:\\Windows\\System32\\Wbem;'
|
|
// 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\'
|
|
// }
|
|
std::vector<fs::path> path;
|
|
if (m_env.find("PATH") != m_env.end())
|
|
{
|
|
auto strings = split(m_env["PATH"], env::pathsep());
|
|
for (auto& s : strings)
|
|
{
|
|
path.push_back(s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (on_linux)
|
|
{
|
|
path = {"/usr/bin"};
|
|
}
|
|
else if (on_mac)
|
|
{
|
|
path = {"/usr/bin", "/bin", "/usr/sbin", "/sbin"};
|
|
}
|
|
else
|
|
{
|
|
path = {
|
|
"C:\\Windows\\system32",
|
|
"C:\\Windows",
|
|
"C:\\Windows\\System32\\Wbem",
|
|
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\"
|
|
};
|
|
}
|
|
}
|
|
// We used to prepend sys.prefix\Library\bin to PATH on startup but not anymore.
|
|
// Instead, in conda 4.6 we add the full suite of entries. This is performed in
|
|
// condabin\conda.bat and condabin\ _conda_activate.bat. However, we
|
|
// need to ignore the stuff we add there, and only consider actual PATH entries.
|
|
|
|
// prefix_dirs = tuple(self._get_path_dirs(sys.prefix))
|
|
// start_index = 0
|
|
// while (start_index < len(prefix_dirs) and
|
|
// start_index < len(path_split) and
|
|
// paths_equal(path_split[start_index], prefix_dirs[start_index])):
|
|
// start_index += 1
|
|
// path_split = path_split[start_index:]
|
|
// library_bin_dir = self.path_conversion(
|
|
// self.sep.join((sys.prefix, 'Library', 'bin')))
|
|
// if paths_equal(path_split[0], library_bin_dir):
|
|
// path_split = path_split[1:]
|
|
// return path_split
|
|
|
|
return path;
|
|
}
|
|
|
|
std::string Activator::add_prefix_to_path(const fs::path& prefix, int old_conda_shlvl)
|
|
{
|
|
// prefix = self.path_conversion(prefix)
|
|
// path_list = list(self.path_conversion(self._get_starting_path_list()))
|
|
auto path_list = get_clean_dirs();
|
|
// If this is the first time we're activating an environment, we need to ensure that
|
|
// the condabin directory is included in the path list.
|
|
// Under normal conditions, if the shell hook is working correctly, this should
|
|
// never trigger.
|
|
if (old_conda_shlvl == 0)
|
|
{
|
|
bool no_condabin = std::none_of(path_list.begin(), path_list.end(),
|
|
[](const std::string& s) { return ends_with(s, "condabin");}
|
|
);
|
|
if (no_condabin)
|
|
{
|
|
auto condabin_dir = Context::instance().conda_prefix / "condabin";
|
|
path_list.insert(path_list.begin(), condabin_dir);
|
|
}
|
|
}
|
|
|
|
// TODO check if path_conversion does something useful here.
|
|
// path_list[0:0] = list(self.path_conversion(self._get_path_dirs(prefix)))
|
|
std::vector<fs::path> final_path = get_path_dirs(prefix);
|
|
final_path.insert(final_path.end(), path_list.begin(), path_list.end());
|
|
final_path.erase(std::unique(final_path.begin(), final_path.end()), final_path.end());
|
|
|
|
std::string result = join(env::pathsep(), final_path);
|
|
return result;
|
|
}
|
|
|
|
std::string Activator::replace_prefix_in_path(const fs::path& old_prefix, const fs::path& new_prefix)
|
|
{
|
|
// TODO not done yet.
|
|
std::vector<fs::path> current_path = get_clean_dirs();
|
|
|
|
assert(!old_prefix.empty());
|
|
std::vector<fs::path> old_prefix_dirs = get_path_dirs(old_prefix);
|
|
|
|
// remove all old paths
|
|
current_path.erase(std::remove_if(current_path.begin(), current_path.end(), [&](const fs::path& path_elem) {
|
|
return std::find_if(old_prefix_dirs.begin(), old_prefix_dirs.end(),
|
|
[&path_elem](const fs::path& old_dir) {
|
|
return paths_equal(old_dir, path_elem);
|
|
}) != old_prefix_dirs.end();
|
|
}));
|
|
|
|
// TODO remove `sys.prefix\Library\bin` on Windows?!
|
|
// Not sure if necessary as we don't fiddle with Python
|
|
|
|
std::vector<fs::path> final_path;
|
|
if (!new_prefix.empty())
|
|
{
|
|
final_path = get_path_dirs(new_prefix);
|
|
final_path.insert(final_path.end(), current_path.begin(), current_path.end());
|
|
// remove duplicates
|
|
final_path.erase(std::unique(final_path.begin(), final_path.end()), final_path.end());
|
|
std::string result = join(env::pathsep(), final_path);
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
current_path.erase(std::unique(current_path.begin(), current_path.end()), current_path.end());
|
|
std::string result = join(env::pathsep(), current_path);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
std::string Activator::remove_prefix_from_path(const fs::path& prefix)
|
|
{
|
|
return replace_prefix_in_path(prefix, fs::path());
|
|
}
|
|
|
|
void Activator::get_export_unset_vars(EnvironmentTransform& envt,
|
|
const std::vector<std::pair<std::string, std::string>>& to_export)
|
|
{
|
|
// conda_exe_vars_export = OrderedDict()
|
|
// for k, v in context.conda_exe_vars_dict.items():
|
|
// if v is None or conda_exe_vars_None:
|
|
// conda_exe_unset_vars.append(k)
|
|
// else:
|
|
// conda_exe_vars_export[k] = self.path_conversion(v) if v else v
|
|
|
|
for (auto& [k, v] : to_export)
|
|
{
|
|
if (v == "")
|
|
{
|
|
envt.unset_vars.push_back(to_upper(k));
|
|
}
|
|
else
|
|
{
|
|
envt.export_vars.push_back({to_upper(k), v});
|
|
}
|
|
}
|
|
}
|
|
EnvironmentTransform Activator::build_reactivate()
|
|
{
|
|
std::string conda_prefix;
|
|
int conda_shlvl = 0;
|
|
if (m_env.find("CONDA_SHLVL") != m_env.end())
|
|
{
|
|
std::string env_shlvl(strip(m_env["CONDA_SHLVL"]));
|
|
conda_shlvl = std::stoi(env_shlvl);
|
|
}
|
|
|
|
if (m_env.find("CONDA_PREFIX") != m_env.end())
|
|
{
|
|
conda_prefix = m_env["CONDA_PREFIX"];
|
|
}
|
|
|
|
EnvironmentTransform envt;
|
|
if (conda_prefix.empty() || conda_shlvl < 1)
|
|
{
|
|
return envt;
|
|
}
|
|
|
|
std::string conda_default_env = (m_env.find("CONDA_DEFAULT_ENV") != m_env.end()) ?
|
|
m_env["CONDA_DEFAULT_ENV"] : get_default_env(conda_prefix);
|
|
|
|
auto new_path = replace_prefix_in_path(conda_prefix, conda_prefix);
|
|
|
|
std::string conda_prompt_modifier = get_prompt_modifier(conda_prefix, conda_default_env, conda_shlvl);
|
|
if (Context::instance().change_ps1)
|
|
{
|
|
auto res = update_prompt(conda_prompt_modifier);
|
|
if (!res.first.empty())
|
|
{
|
|
envt.set_vars.push_back(res);
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> env_vars_to_export = {
|
|
{"path", new_path},
|
|
{"conda_shlvl", std::to_string(conda_shlvl)},
|
|
{"conda_prompt_modifier", conda_prompt_modifier}
|
|
};
|
|
get_export_unset_vars(envt, env_vars_to_export);
|
|
|
|
// TODO figure out if this is all really necessary?
|
|
// # environment variables are set only to aid transition from conda 4.3 to conda 4.4
|
|
// conda_environment_env_vars = self._get_environment_env_vars(conda_prefix)
|
|
// for k, v in conda_environment_env_vars.items():
|
|
// if v == CONDA_ENV_VARS_UNSET_VAR:
|
|
// env_vars_to_unset = env_vars_to_unset + (k,)
|
|
// else:
|
|
// env_vars_to_export[k] = v
|
|
|
|
envt.deactivate_scripts = get_deactivate_scripts(conda_prefix);
|
|
envt.activate_scripts = get_activate_scripts(conda_prefix);
|
|
|
|
return envt;
|
|
}
|
|
|
|
EnvironmentTransform Activator::build_deactivate()
|
|
{
|
|
EnvironmentTransform envt;
|
|
|
|
if (m_env.find("CONDA_PREFIX") == m_env.end() ||
|
|
m_env.find("CONDA_SHLVL") == m_env.end())
|
|
{
|
|
// nothing to deactivate
|
|
return envt;
|
|
}
|
|
std::string old_conda_prefix = m_env["CONDA_PREFIX"];
|
|
int old_conda_shlvl = std::stoi(m_env["CONDA_SHLVL"]);
|
|
envt.deactivate_scripts = get_deactivate_scripts(old_conda_prefix);
|
|
auto old_conda_environment_env_vars = get_environment_vars(old_conda_prefix);
|
|
int new_conda_shlvl = old_conda_shlvl - 1;
|
|
|
|
std::string conda_prompt_modifier = "";
|
|
// set_vars = {};
|
|
if (old_conda_shlvl == 1)
|
|
{
|
|
std::string new_path = remove_prefix_from_path(old_conda_prefix);
|
|
// You might think that you can remove the CONDA_EXE vars by passing conda_exe_vars=None
|
|
// here so that "deactivate means deactivate" but you cannot since the conda shell
|
|
// scripts still refer to them and they only set them once at the top. We could change
|
|
// that though, the conda() shell function could set them instead of doing it at the
|
|
// top. This would be *much* cleaner. I personally cannot abide that I have
|
|
// deactivated conda and anything at all in my env still references it (apart from the
|
|
// shell script, we need something I suppose!)
|
|
envt.export_path = new_path;
|
|
std::vector<std::pair<std::string, std::string>> env_vars_to_export = {
|
|
{"conda_prefix", ""},
|
|
{"conda_shlvl", std::to_string(new_conda_shlvl)},
|
|
{"conda_default_env", ""},
|
|
{"conda_prompt_modifier", ""}
|
|
};
|
|
get_export_unset_vars(envt, env_vars_to_export);
|
|
}
|
|
else
|
|
{
|
|
assert (old_conda_shlvl > 1);
|
|
std::string new_prefix = m_env.at("CONDA_PREFIX_" + std::to_string(new_conda_shlvl));
|
|
std::string conda_default_env = get_default_env(new_prefix);
|
|
std::string conda_prompt_modifier = get_prompt_modifier(new_prefix, conda_default_env, old_conda_shlvl);
|
|
auto new_conda_environment_env_vars = get_environment_vars(new_prefix);
|
|
|
|
bool old_prefix_stacked = (m_env.find("CONDA_STACKED_" + std::to_string(old_conda_shlvl)) != m_env.end());
|
|
std::string new_path;
|
|
envt.unset_vars.push_back("CONDA_PREFIX_" + std::to_string(new_conda_shlvl));
|
|
|
|
if (old_prefix_stacked)
|
|
{
|
|
new_path = remove_prefix_from_path(old_conda_prefix);
|
|
envt.unset_vars.push_back("CONDA_STACKED_" + std::to_string(old_conda_shlvl));
|
|
}
|
|
else
|
|
{
|
|
new_path = replace_prefix_in_path(old_conda_prefix, new_prefix);
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> env_vars_to_export = {
|
|
{"conda_prefix", "new_prefix"},
|
|
{"conda_shlvl", std::to_string(new_conda_shlvl)},
|
|
{"conda_default_env", conda_default_env},
|
|
{"conda_prompt_modifier", conda_prompt_modifier}
|
|
};
|
|
|
|
get_export_unset_vars(envt, env_vars_to_export);
|
|
|
|
for (auto& [k, v] : new_conda_environment_env_vars)
|
|
{
|
|
envt.export_vars.push_back({k, v});
|
|
}
|
|
|
|
envt.export_path = new_path;
|
|
envt.activate_scripts = get_activate_scripts(new_prefix);
|
|
}
|
|
|
|
if (Context::instance().change_ps1)
|
|
{
|
|
auto res = update_prompt(conda_prompt_modifier);
|
|
if (!res.first.empty())
|
|
{
|
|
envt.set_vars.push_back(res);
|
|
}
|
|
}
|
|
|
|
for (auto& env_var : old_conda_environment_env_vars)
|
|
{
|
|
envt.unset_vars.push_back(env_var.first);
|
|
std::string save_var = std::string("__CONDA_SHLVL_") + std::to_string(new_conda_shlvl) + "_" + env_var.first; // % (new_conda_shlvl, env_var)
|
|
if (m_env.find(save_var) != m_env.end())
|
|
{
|
|
envt.export_vars.push_back({env_var.first, m_env[save_var]});
|
|
}
|
|
}
|
|
return envt;
|
|
}
|
|
|
|
EnvironmentTransform Activator::build_activate(const fs::path& prefix)
|
|
{
|
|
EnvironmentTransform envt;
|
|
|
|
// TODO find prefix if not absolute path
|
|
// if re.search(r'\\|/', env_name_or_prefix):
|
|
// prefix = expand(env_name_or_prefix)
|
|
// if not isdir(join(prefix, 'conda-meta')):
|
|
// from .exceptions import EnvironmentLocationNotFound
|
|
// raise EnvironmentLocationNotFound(prefix)
|
|
// elif env_name_or_prefix in (ROOT_ENV_NAME, 'root'):
|
|
// prefix = context.root_prefix
|
|
// else:
|
|
// prefix = locate_prefix_by_name(env_name_or_prefix)
|
|
|
|
// query environment
|
|
std::string old_conda_prefix;
|
|
int old_conda_shlvl = 0, new_conda_shlvl;
|
|
if (m_env.find("CONDA_SHLVL") != m_env.end())
|
|
{
|
|
std::string env_shlvl(strip(m_env["CONDA_SHLVL"]));
|
|
old_conda_shlvl = std::stoi(env_shlvl);
|
|
}
|
|
|
|
new_conda_shlvl = old_conda_shlvl + 1;
|
|
if (m_env.find("CONDA_PREFIX") != m_env.end())
|
|
{
|
|
old_conda_prefix = m_env["CONDA_PREFIX"];
|
|
}
|
|
|
|
if (old_conda_prefix == prefix && old_conda_shlvl > 0)
|
|
{
|
|
return build_reactivate();
|
|
}
|
|
|
|
if (old_conda_shlvl &&
|
|
m_env.find("CONDA_PREFIX_" + std::to_string(old_conda_shlvl - 1)) != m_env.end() &&
|
|
m_env["CONDA_PREFIX_" + std::to_string(old_conda_shlvl - 1)] == prefix)
|
|
{
|
|
// in this case, user is attempting to activate the previous environment,
|
|
// i.e. step back down
|
|
return build_deactivate();
|
|
}
|
|
|
|
envt.activate_scripts = get_activate_scripts(prefix);
|
|
std::string conda_default_env = get_default_env(prefix);
|
|
std::string conda_prompt_modifier = get_prompt_modifier(prefix, conda_default_env, old_conda_shlvl);
|
|
|
|
auto conda_environment_env_vars = get_environment_vars(prefix);
|
|
|
|
// TODO
|
|
// unset_env_vars = [k for k, v in conda_environment_env_vars.items()
|
|
// if v == CONDA_ENV_VARS_UNSET_VAR]
|
|
// [conda_environment_env_vars.pop(_) for _ in unset_env_vars]
|
|
|
|
std::vector<std::string> clobbering_env_vars;
|
|
for (auto& env_var : conda_environment_env_vars)
|
|
{
|
|
if (m_env.find(env_var.first) != m_env.end()) clobbering_env_vars.push_back(env_var.first);
|
|
}
|
|
|
|
for (const auto& v : clobbering_env_vars)
|
|
{
|
|
// TODO use concat
|
|
std::stringstream tmp;
|
|
tmp << "__CONDA_SHLVL_" << old_conda_shlvl << "_" << v;
|
|
conda_environment_env_vars.push_back({tmp.str(), m_env[v]});
|
|
}
|
|
|
|
if (clobbering_env_vars.size())
|
|
{
|
|
LOG_WARNING << "WARNING: overwriting environment variables set in the machine";
|
|
LOG_WARNING << "Overwriting variables: " << join(",", clobbering_env_vars);
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> env_vars_to_export;
|
|
std::vector<std::string> unset_vars;
|
|
|
|
std::string new_path = add_prefix_to_path(prefix, old_conda_shlvl);
|
|
|
|
env_vars_to_export = {
|
|
{"path", std::string(new_path)},
|
|
{"conda_prefix", std::string(prefix)},
|
|
{"conda_shlvl", std::to_string(new_conda_shlvl)},
|
|
{"conda_default_env", conda_default_env},
|
|
{"conda_prompt_modifier", conda_prompt_modifier}
|
|
};
|
|
|
|
for (auto& [k, v] : conda_environment_env_vars)
|
|
{
|
|
envt.export_vars.push_back({k, v});
|
|
}
|
|
|
|
if (old_conda_shlvl == 0)
|
|
{
|
|
get_export_unset_vars(envt, env_vars_to_export);
|
|
}
|
|
else
|
|
{
|
|
if (m_stack)
|
|
{
|
|
get_export_unset_vars(envt, env_vars_to_export);
|
|
envt.export_vars.push_back({"CONDA_PREFIX_" + std::to_string(old_conda_shlvl), old_conda_prefix});
|
|
envt.export_vars.push_back({"CONDA_STACKED_" + std::to_string(new_conda_shlvl), "true"});
|
|
}
|
|
else
|
|
{
|
|
new_path = replace_prefix_in_path(old_conda_prefix, prefix);
|
|
envt.deactivate_scripts = get_deactivate_scripts(old_conda_prefix);
|
|
env_vars_to_export[0] = {"PATH", new_path};
|
|
get_export_unset_vars(envt, env_vars_to_export);
|
|
envt.export_vars.push_back({"CONDA_PREFIX_" + std::to_string(old_conda_shlvl), old_conda_prefix});
|
|
}
|
|
}
|
|
|
|
if (Context::instance().change_ps1)
|
|
{
|
|
auto res = update_prompt(conda_prompt_modifier);
|
|
if (!res.first.empty())
|
|
{
|
|
envt.set_vars.push_back(res);
|
|
}
|
|
}
|
|
|
|
return envt;
|
|
}
|
|
|
|
std::string Activator::activate(const fs::path& prefix, bool stack)
|
|
{
|
|
m_stack = stack;
|
|
m_action = ActivationType::ACTIVATE;
|
|
return script(build_activate(prefix));
|
|
}
|
|
|
|
std::string Activator::reactivate()
|
|
{
|
|
m_action = ActivationType::REACTIVATE;
|
|
return script(build_reactivate());
|
|
}
|
|
|
|
std::string Activator::deactivate()
|
|
{
|
|
m_action = ActivationType::DEACTIVATE;
|
|
return script(build_deactivate());
|
|
}
|
|
|
|
std::string Activator::hook()
|
|
{
|
|
std::stringstream builder;
|
|
builder << hook_preamble() << "\n";
|
|
if (!hook_source_path().empty())
|
|
{
|
|
builder << read_contents(hook_source_path()) << "\n";
|
|
}
|
|
if (Context::instance().auto_activate_base)
|
|
{
|
|
builder << "mamba activate base\n";
|
|
}
|
|
builder << hook_postamble() << "\n";
|
|
return builder.str();
|
|
}
|
|
|
|
/*********************************
|
|
* PosixActivator implementation *
|
|
*********************************/
|
|
|
|
std::string PosixActivator::script(const EnvironmentTransform& env_transform)
|
|
{
|
|
std::stringstream out;
|
|
|
|
if (!env_transform.export_path.empty())
|
|
{
|
|
out << "export PATH='" << env_transform.export_path << "'\n";
|
|
}
|
|
|
|
for (const fs::path& ds : env_transform.deactivate_scripts)
|
|
{
|
|
out << ". " << ds << "\n";
|
|
}
|
|
|
|
for (const std::string& uvar : env_transform.unset_vars)
|
|
{
|
|
out << "unset " << uvar << "\n";
|
|
}
|
|
|
|
for (const auto& [skey, svar] : env_transform.set_vars)
|
|
{
|
|
out << skey << "='" << svar << "'\n";
|
|
}
|
|
|
|
for (const auto& [ekey, evar] : env_transform.export_vars)
|
|
{
|
|
out << "export " << ekey << "='" << evar << "'\n";
|
|
}
|
|
|
|
for (const fs::path& p : env_transform.activate_scripts)
|
|
{
|
|
out << ". " << p << "\n";
|
|
}
|
|
|
|
return out.str();
|
|
}
|
|
|
|
std::pair<std::string, std::string>
|
|
PosixActivator::update_prompt(const std::string& conda_prompt_modifier)
|
|
{
|
|
std::string ps1 = (m_env.find("PS1") != m_env.end()) ? m_env["PS1"] : "";
|
|
if (ps1.find("POWERLINE_COMMAND") != ps1.npos)
|
|
{
|
|
// Defer to powerline (https://github.com/powerline/powerline) if it's in use.
|
|
return {"", ""};
|
|
}
|
|
std::string current_prompt_modifier = env::get("CONDA_PROMPT_MODIFIER");
|
|
if (!current_prompt_modifier.empty())
|
|
{
|
|
replace_all(ps1, current_prompt_modifier, "");
|
|
}
|
|
// Because we're using single-quotes to set shell variables, we need to handle the
|
|
// proper escaping of single quotes that are already part of the string.
|
|
// Best solution appears to be https://stackoverflow.com/a/1250279
|
|
replace_all(ps1, "'", "'\"'\"'");
|
|
return {"PS1", conda_prompt_modifier + ps1};
|
|
}
|
|
|
|
std::string PosixActivator::shell_extension()
|
|
{
|
|
return ".sh";
|
|
}
|
|
|
|
std::string PosixActivator::hook_preamble()
|
|
{
|
|
// result = ''
|
|
// for key, value in context.conda_exe_vars_dict.items():
|
|
// if value is None:
|
|
// # Using `unset_var_tmpl` would cause issues for people running
|
|
// # with shell flag -u set (error on unset).
|
|
// # result += join(self.unset_var_tmpl % key) + '\n'
|
|
// result += join(self.export_var_tmpl % (key, '')) + '\n'
|
|
// else:
|
|
// if key in ('PYTHONPATH', 'CONDA_EXE'):
|
|
// result += join(self.export_var_tmpl % (
|
|
// key, self.path_conversion(value))) + '\n'
|
|
// else:
|
|
// result += join(self.export_var_tmpl % (key, value)) + '\n'
|
|
// return result
|
|
std::string preamble;
|
|
return preamble;
|
|
}
|
|
|
|
std::string PosixActivator::hook_postamble()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
fs::path PosixActivator::hook_source_path()
|
|
{
|
|
return Context::instance().root_prefix / "etc" / "profile.d" / "mamba.sh";
|
|
}
|
|
|
|
std::string CmdExeActivator::shell_extension()
|
|
{
|
|
return ".bat";
|
|
}
|
|
|
|
std::string CmdExeActivator::hook_preamble()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
std::string CmdExeActivator::hook_postamble()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
fs::path CmdExeActivator::hook_source_path()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
std::pair<std::string, std::string> CmdExeActivator::update_prompt(const std::string& conda_prompt_modifier)
|
|
{
|
|
return {"", ""};
|
|
}
|
|
|
|
std::string CmdExeActivator::script(const EnvironmentTransform& env_transform)
|
|
{
|
|
TemporaryFile* tempfile_ptr = new TemporaryFile("mamba_act", ".bat");
|
|
std::stringstream out;
|
|
|
|
if (!env_transform.export_path.empty())
|
|
{
|
|
out << "export PATH='" << env_transform.export_path << "'\n";
|
|
}
|
|
|
|
for (const fs::path& ds : env_transform.deactivate_scripts)
|
|
{
|
|
out << "@CALL " << ds << "\n";
|
|
}
|
|
|
|
for (const std::string& uvar : env_transform.unset_vars)
|
|
{
|
|
out << "@SET " << uvar << "=\n";
|
|
}
|
|
|
|
for (const auto& [skey, svar] : env_transform.set_vars)
|
|
{
|
|
out << "@SET \"" << skey << "=" << svar << "\"\n";
|
|
}
|
|
|
|
for (const auto& [ekey, evar] : env_transform.export_vars)
|
|
{
|
|
out << "@SET \"" << ekey << "=" << evar << "\"\n";
|
|
}
|
|
|
|
for (const fs::path& p : env_transform.activate_scripts)
|
|
{
|
|
out << "@CALL " << p << "\n";
|
|
}
|
|
|
|
std::ofstream out_file(tempfile_ptr->path());
|
|
out_file << out.str();
|
|
// note: we do not delete the tempfile ptr intentionally, so that the temp file stays
|
|
return tempfile_ptr->path().string();
|
|
}
|
|
|
|
std::string PowerShellActivator::shell_extension()
|
|
{
|
|
return ".ps1";
|
|
}
|
|
|
|
std::string PowerShellActivator::hook_preamble()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
std::string PowerShellActivator::hook_postamble()
|
|
{
|
|
if (Context::instance().change_ps1)
|
|
{
|
|
return "Add-CondaEnvironmentToPrompt";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
fs::path PowerShellActivator::hook_source_path()
|
|
{
|
|
return Context::instance().root_prefix / "condabin" / "mamba_hook.ps1";
|
|
}
|
|
|
|
std::pair<std::string, std::string> PowerShellActivator::update_prompt(const std::string& conda_prompt_modifier)
|
|
{
|
|
return {"", ""};
|
|
}
|
|
|
|
std::string PowerShellActivator::script(const EnvironmentTransform& env_transform)
|
|
{
|
|
std::stringstream out;
|
|
|
|
if (!env_transform.export_path.empty())
|
|
{
|
|
out << "$Env:PATH =\"" << env_transform.export_path << "\"\n";
|
|
}
|
|
|
|
for (const fs::path& ds : env_transform.deactivate_scripts)
|
|
{
|
|
out << ". " << ds << "\n";
|
|
}
|
|
|
|
for (const std::string& uvar : env_transform.unset_vars)
|
|
{
|
|
out << "Remove-Item Env:/" << uvar << "\n";
|
|
}
|
|
|
|
for (const auto& [skey, svar] : env_transform.set_vars)
|
|
{
|
|
out << "$Env:" << skey << " = \"" << svar << "\"\n";
|
|
}
|
|
|
|
for (const auto& [ekey, evar] : env_transform.export_vars)
|
|
{
|
|
out << "$Env:" << ekey << " = \"" << evar << "\"\n";
|
|
}
|
|
|
|
for (const fs::path& p : env_transform.activate_scripts)
|
|
{
|
|
out << ". " << p << "\n";
|
|
}
|
|
|
|
return out.str();
|
|
}
|
|
|
|
}
|
|
|