diff --git a/libmamba/include/mamba/core/context.hpp b/libmamba/include/mamba/core/context.hpp index 0d5ea0e2dc..d35ab0ea4a 100644 --- a/libmamba/include/mamba/core/context.hpp +++ b/libmamba/include/mamba/core/context.hpp @@ -129,6 +129,7 @@ namespace mamba fs::u8path target_prefix; fs::u8path root_prefix; fs::u8path conda_prefix; + fs::u8path relocate_prefix; // TODO check writable and add other potential dirs std::vector envs_dirs; diff --git a/libmamba/include/mamba/core/transaction_context.hpp b/libmamba/include/mamba/core/transaction_context.hpp index a13a7cfef2..038bb67477 100644 --- a/libmamba/include/mamba/core/transaction_context.hpp +++ b/libmamba/include/mamba/core/transaction_context.hpp @@ -39,12 +39,20 @@ namespace mamba const std::pair& py_versions, const std::vector& requested_specs ); + + TransactionContext( + const fs::u8path& target_prefix, + const fs::u8path& relocate_prefix, + const std::pair& py_versions, + const std::vector& requested_specs + ); ~TransactionContext(); bool try_pyc_compilation(const std::vector& py_files); void wait_for_pyc_compilation(); bool has_python; fs::u8path target_prefix; + fs::u8path relocate_prefix; fs::u8path site_packages_path; fs::u8path python_path; std::string python_version; diff --git a/libmamba/src/api/configuration.cpp b/libmamba/src/api/configuration.cpp index 37996768f6..98dd0862ee 100644 --- a/libmamba/src/api/configuration.cpp +++ b/libmamba/src/api/configuration.cpp @@ -1036,6 +1036,13 @@ namespace mamba .set_post_merge_hook(detail::target_prefix_hook) .set_post_context_hook(detail::post_target_prefix_rc_loading)); + insert(Configurable("relocate_prefix", &ctx.relocate_prefix) + .group("Basic") + .set_env_var_names() + .needs({ "target_prefix" }) + .set_single_op_lifetime() + .description("Path to the relocation prefix")); + insert(Configurable("use_target_prefix_fallback", true) .group("Basic") .set_single_op_lifetime() diff --git a/libmamba/src/core/link.cpp b/libmamba/src/core/link.cpp index 74d8608378..5f6378ceef 100644 --- a/libmamba/src/core/link.cpp +++ b/libmamba/src/core/link.cpp @@ -157,7 +157,7 @@ namespace mamba fs::u8path python_path; if (m_context->has_python) { - python_path = m_context->target_prefix / m_context->python_path; + python_path = m_context->relocate_prefix / m_context->python_path; } if (!python_path.empty()) { @@ -614,7 +614,7 @@ namespace mamba { // we have to replace the PREFIX stuff in the data // and copy the file - std::string new_prefix = m_context->target_prefix.string(); + std::string new_prefix = m_context->relocate_prefix.string(); #ifdef _WIN32 replace_all(new_prefix, "\\", "/"); #endif diff --git a/libmamba/src/core/transaction.cpp b/libmamba/src/core/transaction.cpp index 344bc19023..c02e5cd31d 100644 --- a/libmamba/src/core/transaction.cpp +++ b/libmamba/src/core/transaction.cpp @@ -576,6 +576,7 @@ namespace mamba m_transaction_context = TransactionContext( Context::instance().target_prefix, + Context::instance().relocate_prefix, find_python_version(), specs_to_install ); @@ -669,6 +670,7 @@ namespace mamba m_transaction_context = TransactionContext( Context::instance().target_prefix, + Context::instance().relocate_prefix, find_python_version(), solver.install_specs() ); @@ -815,6 +817,7 @@ namespace mamba m_transaction_context = TransactionContext( Context::instance().target_prefix, + Context::instance().relocate_prefix, find_python_version(), specs_to_install ); diff --git a/libmamba/src/core/transaction_context.cpp b/libmamba/src/core/transaction_context.cpp index 448bab08b4..cf3cb2fb0b 100644 --- a/libmamba/src/core/transaction_context.cpp +++ b/libmamba/src/core/transaction_context.cpp @@ -101,6 +101,7 @@ namespace mamba ) : has_python(py_versions.first.size() != 0) , target_prefix(ltarget_prefix) + , relocate_prefix(ltarget_prefix) , python_version(py_versions.first) , old_python_version(py_versions.second) , requested_specs(lrequested_specs) @@ -133,12 +134,31 @@ namespace mamba } } + TransactionContext::TransactionContext( + const fs::u8path& ltarget_prefix, + const fs::u8path& lrelocate_prefix, + const std::pair& py_versions, + const std::vector& lrequested_specs + ) + : TransactionContext(ltarget_prefix, py_versions, lrequested_specs) + { + if (lrelocate_prefix.empty()) + { + relocate_prefix = ltarget_prefix; + } + else + { + relocate_prefix = lrelocate_prefix; + } + } + TransactionContext& TransactionContext::operator=(const TransactionContext& other) { if (this != &other) { has_python = other.has_python; target_prefix = other.target_prefix; + relocate_prefix = other.relocate_prefix; python_version = other.python_version; old_python_version = other.old_python_version; requested_specs = other.requested_specs; diff --git a/micromamba/src/common_options.cpp b/micromamba/src/common_options.cpp index 0dcea20f24..97461e0a79 100644 --- a/micromamba/src/common_options.cpp +++ b/micromamba/src/common_options.cpp @@ -118,6 +118,15 @@ init_prefix_options(CLI::App* subcom) subcom->add_option("-p,--prefix", prefix.get_cli_config(), prefix.description()) ->group(cli_group); + auto& relocate_prefix = config.at("relocate_prefix"); + subcom + ->add_option( + "--relocate-prefix", + relocate_prefix.get_cli_config(), + relocate_prefix.description() + ) + ->group(cli_group); + auto& name = config.at("env_name"); subcom->add_option("-n,--name", name.get_cli_config(), name.description()) ->group(cli_group); diff --git a/micromamba/tests/test_create.py b/micromamba/tests/test_create.py index f4c3b28d88..79af86846c 100644 --- a/micromamba/tests/test_create.py +++ b/micromamba/tests/test_create.py @@ -31,7 +31,6 @@ class TestCreate: - current_root_prefix = os.environ["MAMBA_ROOT_PREFIX"] current_prefix = os.environ["CONDA_PREFIX"] @@ -475,7 +474,6 @@ def test_explicit_specs(self, valid, existing_cache): @pytest.mark.parametrize("prefix_selector", [None, "prefix", "name"]) @pytest.mark.parametrize("create_cmd", ["create", "env create"]) def test_create_empty(self, prefix_selector, existing_cache, create_cmd): - if prefix_selector == "name": cmd = ("-n", TestCreate.env_name, "--json") elif prefix_selector == "prefix": @@ -493,6 +491,26 @@ def test_create_empty(self, prefix_selector, existing_cache, create_cmd): assert Path(os.path.join(TestCreate.prefix, "conda-meta", "history")).exists() + @pytest.mark.skipif( + dry_run_tests is DryRun.ULTRA_DRY, reason="Running only ultra-dry tests" + ) + @pytest.mark.parametrize("relocate_prefix", ["/home/bob/env", "/"]) + def test_create_with_relocate_prefix(self, relocate_prefix, existing_cache): + res = create( + "-p", + TestCreate.prefix, + "--relocate-prefix", + relocate_prefix, + "python=3.11", + "--json", + no_dry_run=True, + ) + assert res["success"] + if platform.system() != "Windows": + with open(Path(TestCreate.prefix) / "bin" / "2to3") as f: + firstline = f.readline() + assert firstline == f"#!{relocate_prefix}/bin/python3.11\n" + @pytest.mark.skipif( dry_run_tests is DryRun.ULTRA_DRY, reason="Running only ultra-dry tests" )