mirror of https://github.com/mamba-org/mamba.git
Fix windows paths and add tests (#3787)
This commit is contained in:
parent
402b2d474b
commit
211d79db77
|
@ -73,11 +73,16 @@ namespace mamba::fs
|
|||
{
|
||||
};
|
||||
|
||||
struct Utf8Options
|
||||
{
|
||||
bool normalize_sep = true;
|
||||
};
|
||||
|
||||
// Maintain `\` on Windows, `/` on other platforms
|
||||
std::filesystem::path normalized_separators(std::filesystem::path path);
|
||||
|
||||
// Returns a UTF-8 string given a standard path.
|
||||
std::string to_utf8(const std::filesystem::path& path);
|
||||
std::string to_utf8(const std::filesystem::path& path, Utf8Options utf8_options = {});
|
||||
|
||||
// Returns standard path given a UTF-8 string.
|
||||
std::filesystem::path from_utf8(std::string_view u8string);
|
||||
|
@ -300,10 +305,10 @@ namespace mamba::fs
|
|||
|
||||
//---- Conversions ----
|
||||
|
||||
// Returns a UTF-8 string.
|
||||
// Returns a UTF-8 string with normalized separators.
|
||||
std::string string() const
|
||||
{
|
||||
return to_utf8(m_path);
|
||||
return to_utf8(m_path, { /*normalize_sep=*/true });
|
||||
}
|
||||
|
||||
// Returns a default encoded string.
|
||||
|
@ -333,7 +338,7 @@ namespace mamba::fs
|
|||
// Returns a UTF-8 string using the ``/`` on all systems.
|
||||
std::string generic_string() const
|
||||
{
|
||||
return to_utf8(m_path.generic_string());
|
||||
return to_utf8(m_path.generic_string(), { /*normalize_sep=*/false });
|
||||
}
|
||||
|
||||
// Implicit conversion to standard path.
|
||||
|
|
|
@ -147,6 +147,7 @@ namespace mamba
|
|||
// We add -script.py to WIN32, and link the conda.exe launcher which will
|
||||
// automatically find the correct script to launch
|
||||
std::string win_script = path.string() + "-script.py";
|
||||
std::string win_script_gen_str = path.generic_string() + "-script.py";
|
||||
fs::u8path script_path = m_context->target_prefix / win_script;
|
||||
#else
|
||||
fs::u8path script_path = m_context->target_prefix / path;
|
||||
|
@ -192,7 +193,7 @@ namespace mamba
|
|||
conda_exe_f.write(reinterpret_cast<char*>(conda_exe), conda_exe_len);
|
||||
conda_exe_f.close();
|
||||
make_executable(m_context->target_prefix / script_exe);
|
||||
return std::array<std::string, 2>{ win_script, script_exe.string() };
|
||||
return std::array<std::string, 2>{ win_script_gen_str, script_exe.generic_string() };
|
||||
#else
|
||||
if (!python_path.empty())
|
||||
{
|
||||
|
@ -607,7 +608,7 @@ namespace mamba
|
|||
// Sometimes we might want to raise here ...
|
||||
m_clobber_warnings.push_back(rel_dst.string());
|
||||
#ifdef _WIN32
|
||||
return std::make_tuple(std::string(validation::sha256sum(dst)), rel_dst.string());
|
||||
return std::make_tuple(std::string(validation::sha256sum(dst)), rel_dst.generic_string());
|
||||
#endif
|
||||
fs::remove(dst);
|
||||
}
|
||||
|
@ -699,7 +700,10 @@ namespace mamba
|
|||
fo << launcher << shebang << (buffer.c_str() + arc_pos);
|
||||
fo.close();
|
||||
}
|
||||
return std::make_tuple(std::string(validation::sha256sum(dst)), rel_dst.string());
|
||||
return std::make_tuple(
|
||||
std::string(validation::sha256sum(dst)),
|
||||
rel_dst.generic_string()
|
||||
);
|
||||
}
|
||||
#else
|
||||
std::size_t padding_size = (path_data.prefix_placeholder.size() > new_prefix.size())
|
||||
|
@ -748,7 +752,7 @@ namespace mamba
|
|||
codesign(dst, m_context->context().output_params.verbosity > 1);
|
||||
}
|
||||
#endif
|
||||
return std::make_tuple(std::string(validation::sha256sum(dst)), rel_dst.string());
|
||||
return std::make_tuple(std::string(validation::sha256sum(dst)), rel_dst.generic_string());
|
||||
}
|
||||
|
||||
if ((path_data.path_type == PathType::HARDLINK) || path_data.no_link)
|
||||
|
@ -800,7 +804,7 @@ namespace mamba
|
|||
fs::copy_symlink(src, dst);
|
||||
// we need to wait until all files are linked to compute the SHA256 sum!
|
||||
// otherwise the file that's pointed to might not be linked yet.
|
||||
return std::make_tuple("", rel_dst.string());
|
||||
return std::make_tuple("", rel_dst.generic_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -811,7 +815,7 @@ namespace mamba
|
|||
}
|
||||
return std::make_tuple(
|
||||
path_data.sha256.empty() ? std::string(validation::sha256sum(dst)) : path_data.sha256,
|
||||
rel_dst.string()
|
||||
rel_dst.generic_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1032,10 +1036,10 @@ namespace mamba
|
|||
std::vector<fs::u8path> pyc_files = compile_pyc_files(for_compilation);
|
||||
for (const fs::u8path& pyc_path : pyc_files)
|
||||
{
|
||||
out_json["paths_data"]["paths"].push_back({ { "_path", pyc_path.string() },
|
||||
out_json["paths_data"]["paths"].push_back({ { "_path", pyc_path.generic_string() },
|
||||
{ "path_type", "pyc_file" } });
|
||||
|
||||
out_json["files"].push_back(pyc_path.string());
|
||||
out_json["files"].push_back(pyc_path.generic_string());
|
||||
}
|
||||
|
||||
if (link_json.find("noarch") != link_json.end()
|
||||
|
|
|
@ -45,9 +45,16 @@ namespace mamba::fs
|
|||
#endif
|
||||
|
||||
#if __cplusplus == 201703L
|
||||
std::string to_utf8(const std::filesystem::path& path)
|
||||
std::string to_utf8(const std::filesystem::path& path, Utf8Options utf8_options)
|
||||
{
|
||||
return normalized_separators(path).u8string();
|
||||
if (utf8_options.normalize_sep)
|
||||
{
|
||||
return normalized_separators(path).u8string();
|
||||
}
|
||||
else
|
||||
{
|
||||
return path.u8string();
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path from_utf8(std::string_view u8string)
|
||||
|
|
|
@ -90,6 +90,7 @@ set(
|
|||
src/core/test_shell_init.cpp
|
||||
src/core/test_tasksync.cpp
|
||||
src/core/test_thread_utils.cpp
|
||||
src/core/test_transaction_context.cpp
|
||||
src/core/test_util.cpp
|
||||
src/core/test_virtual_packages.cpp
|
||||
)
|
||||
|
|
|
@ -37,6 +37,68 @@ namespace mamba
|
|||
REQUIRE(y.u8string() == u8"日本語");
|
||||
}
|
||||
|
||||
TEST_CASE("to_utf8_check_separators")
|
||||
{
|
||||
static constexpr auto some_path_str = u8"a/b/c";
|
||||
std::filesystem::path some_path = std::filesystem::u8path(some_path_str);
|
||||
|
||||
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/false }) == some_path_str);
|
||||
#if defined(_WIN32)
|
||||
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == u8"a\\b\\c");
|
||||
#else
|
||||
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == some_path_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("to_utf8_check_separators_unicode")
|
||||
{
|
||||
static constexpr auto some_path_str = u8"日/本/語";
|
||||
std::filesystem::path some_path = std::filesystem::u8path(some_path_str);
|
||||
|
||||
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/false }) == some_path_str);
|
||||
#if defined(_WIN32)
|
||||
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == u8"日\\本\\語");
|
||||
#else
|
||||
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == some_path_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("from_utf8_check_separators")
|
||||
{
|
||||
static constexpr auto some_path_str = u8"a/b/c";
|
||||
|
||||
#if defined(_WIN32)
|
||||
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"a\\b\\c"));
|
||||
#else
|
||||
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"a/b/c"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("from_utf8_check_separators_unicode")
|
||||
{
|
||||
static constexpr auto some_path_str = u8"日/本/語";
|
||||
|
||||
#if defined(_WIN32)
|
||||
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"日\\本\\語"));
|
||||
#else
|
||||
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"日/本/語"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("u8path_separators_formatting")
|
||||
{
|
||||
static constexpr auto some_path_str = u8"a/b/c";
|
||||
std::filesystem::path some_path = std::filesystem::u8path(some_path_str);
|
||||
const fs::u8path u8_path(some_path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
REQUIRE(u8_path.string() == u8"a\\b\\c");
|
||||
#else
|
||||
REQUIRE(u8_path.string() == some_path_str);
|
||||
#endif
|
||||
REQUIRE(u8_path.generic_string() == some_path_str);
|
||||
}
|
||||
|
||||
TEST_CASE("consistent_encoding")
|
||||
{
|
||||
const auto utf8_string = u8"日本語";
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2025, 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 <catch2/catch_all.hpp>
|
||||
|
||||
#include "mamba/core/transaction_context.hpp"
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("compute_short_python_version")
|
||||
{
|
||||
REQUIRE(compute_short_python_version("") == "");
|
||||
REQUIRE(compute_short_python_version("3.5") == "3.5");
|
||||
REQUIRE(compute_short_python_version("3.5.0") == "3.5");
|
||||
}
|
||||
|
||||
TEST_CASE("get_python_short_path")
|
||||
{
|
||||
auto path_empty_ver = get_python_short_path("").string();
|
||||
auto path = get_python_short_path("3.5.0").string();
|
||||
#ifdef _WIN32
|
||||
REQUIRE(path_empty_ver == "python.exe");
|
||||
REQUIRE(path == "python.exe");
|
||||
#else
|
||||
REQUIRE(path_empty_ver == "bin/python");
|
||||
REQUIRE(path == "bin/python3.5.0");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("get_python_site_packages_short_path")
|
||||
{
|
||||
REQUIRE(get_python_site_packages_short_path("").string() == "");
|
||||
|
||||
auto path = get_python_site_packages_short_path("3.5.0");
|
||||
auto path_str = path.string();
|
||||
auto path_gen_str = path.generic_string();
|
||||
#ifdef _WIN32
|
||||
REQUIRE(path_str == "Lib\\site-packages");
|
||||
REQUIRE(path_gen_str == "Lib/site-packages");
|
||||
#else
|
||||
REQUIRE(path_str == "lib/python3.5.0/site-packages");
|
||||
REQUIRE(path_gen_str == "lib/python3.5.0/site-packages");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("get_bin_directory_short_path")
|
||||
{
|
||||
auto path = get_bin_directory_short_path().string();
|
||||
#ifdef _WIN32
|
||||
REQUIRE(path == "Scripts");
|
||||
#else
|
||||
REQUIRE(path == "bin");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("get_python_noarch_target_path")
|
||||
{
|
||||
auto random_path = get_python_noarch_target_path("some_lib/some_folder", "bla");
|
||||
auto random_path_str = random_path.string();
|
||||
auto random_path_gen_str = random_path.generic_string();
|
||||
|
||||
auto sp_path = get_python_noarch_target_path(
|
||||
"site-packages/some_random_package",
|
||||
"target_site_packages_short_path"
|
||||
);
|
||||
auto sp_path_str = sp_path.string();
|
||||
auto sp_path_gen_str = sp_path.generic_string();
|
||||
|
||||
auto ps_path = get_python_noarch_target_path(
|
||||
"python-scripts/some_random_file",
|
||||
"target_site_packages_short_path"
|
||||
);
|
||||
auto ps_path_str = ps_path.string();
|
||||
auto ps_path_gen_str = ps_path.generic_string();
|
||||
|
||||
REQUIRE(random_path_gen_str == "some_lib/some_folder");
|
||||
REQUIRE(sp_path_gen_str == "target_site_packages_short_path/some_random_package");
|
||||
#ifdef _WIN32
|
||||
REQUIRE(random_path_str == "some_lib\\some_folder");
|
||||
REQUIRE(sp_path_str == "target_site_packages_short_path\\some_random_package");
|
||||
REQUIRE(ps_path_str == "Scripts\\some_random_file");
|
||||
REQUIRE(ps_path_gen_str == "Scripts/some_random_file");
|
||||
#else
|
||||
REQUIRE(random_path_str == "some_lib/some_folder");
|
||||
REQUIRE(sp_path_str == "target_site_packages_short_path/some_random_package");
|
||||
REQUIRE(ps_path_str == "bin/some_random_file");
|
||||
REQUIRE(ps_path_gen_str == "bin/some_random_file");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace mamba
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
|
@ -1437,6 +1438,35 @@ def test_create_from_oci_mirrored_channels_pkg_name_mapping(
|
|||
assert pkg["channel"] == "https://pkg-containers.githubusercontent.com/ghcr1/blobs"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
||||
def test_create_with_norm_path(tmp_home, tmp_root_prefix):
|
||||
env_name = "myenv"
|
||||
env_prefix = tmp_root_prefix / "envs" / env_name
|
||||
|
||||
res = helpers.create("-n", env_name, "conda-smithy", "--json")
|
||||
assert res["success"]
|
||||
|
||||
conda_smithy = next((env_prefix / "conda-meta").glob("conda-smithy*.json")).read_text()
|
||||
conda_smithy_data = json.loads(conda_smithy)
|
||||
if platform.system() == "Windows":
|
||||
assert all(
|
||||
file_path.startswith(("Lib/site-packages", "Scripts"))
|
||||
for file_path in conda_smithy_data["files"]
|
||||
)
|
||||
assert all(
|
||||
py_path["_path"].startswith(("Lib/site-packages", "Scripts"))
|
||||
for py_path in conda_smithy_data["paths_data"]["paths"]
|
||||
)
|
||||
else:
|
||||
assert all(
|
||||
file_path.startswith(("lib/python", "bin")) for file_path in conda_smithy_data["files"]
|
||||
)
|
||||
assert all(
|
||||
py_path["_path"].startswith(("lib/python", "bin"))
|
||||
for py_path in conda_smithy_data["paths_data"]["paths"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
||||
def test_create_with_unicode(tmp_home, tmp_root_prefix):
|
||||
env_name = "320 áγђß家固êôōçñ한"
|
||||
|
|
Loading…
Reference in New Issue