Skip to content
Open
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 .github/workflows/unix_impl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
--preset mamba-unix-shared-${{ inputs.build_type }} \
-D CMAKE_CXX_COMPILER_LAUNCHER=sccache \
-D CMAKE_C_COMPILER_LAUNCHER=sccache \
-D MAMBA_WARNING_AS_ERROR=ON \
-D BUILD_LIBMAMBAPY=OFF \
-D ENABLE_MAMBA_ROOT_PREFIX_FALLBACK=OFF
cmake --build build/ --parallel
Expand Down
7 changes: 2 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ option(BUILD_LIBMAMBA_TESTS "Build libmamba C++ tests" OFF)
option(BUILD_MAMBA "Build mamba" OFF)
option(BUILD_MICROMAMBA "Build micromamba" OFF)
option(BUILD_MAMBA_PACKAGE "Build mamba package utility" OFF)
if(MSVC)
option(MAMBA_WARNING_AS_ERROR "Treat compiler warnings as errors" OFF)
else()
option(MAMBA_WARNING_AS_ERROR "Treat compiler warnings as errors" ON)
endif()
option(MAMBA_WARNING_AS_ERROR "Treat compiler warnings as errors" OFF)

set(
MAMBA_LTO
"Default"
Expand Down
8 changes: 8 additions & 0 deletions libmamba/src/api/update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ namespace mamba
auto outcome = solver::libsolv::Solver().solve(db, request).value();
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&outcome))
{
unsolvable->explain_problems_to(
db,
LOG_ERROR,
{
/* .unavailable= */ ctx.graphics_params.palette.failure,
/* .available= */ ctx.graphics_params.palette.success,
}
);
if (ctx.output_params.json)
{
Console::instance().json_write({ { "success", false },
Expand Down
39 changes: 0 additions & 39 deletions libmamba/src/specs/match_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -545,45 +545,6 @@ namespace mamba::specs
}
}

// Handle PEP 440 "Compatible release" specification
// See: https://peps.python.org/pep-0440/#compatible-release
//
// Find a general replacement of the encoding of `~=` with `>=,.*` to be able to parse it
// properly.
//
// For instance:
//
// "~=x.y" must be replaced to ">=x.y,x.*" where `x` and `y` are positive integers.
//
// This solution must handle the case where the version is encoded with `~=` within the
// specification for instance:
//
// ">1.8,<2|==1.7,!=1.9,~=1.7.1 py34_0"
//
// must be replaced with:
//
// ">1.8,<2|==1.7,!=1.9,>=1.7.1,1.7.* py34_0"
//
while (raw_match_spec_str.find("~=") != std::string::npos)
{
// Extract the string before the `~=` operator (">1.8,<2|==1.7,!=1.9," for the above
// example)
const auto before = raw_match_spec_str.substr(0, str.find("~="));
// Extract the string after the `~=` operator (include `~=` in it) and the next operator
// space or end of the string ("~=1.7.1 py34_0" for the above example)
const auto after = raw_match_spec_str.substr(str.find("~="));
// Extract the version part after the `~=` operator ("1.7.1" for the above example)
const auto version = after.substr(2, after.find_first_of(" ,") - 2);
// Extract the version part without the last segment ("1.7" for the above example)
const auto version_without_last_segment = version.substr(0, version.find_last_of('.'));
// Extract the build part after the version part (" py34_0" for the above example) if
// present
const auto build = after.find(" ") != std::string::npos ? after.substr(after.find(" "))
: "";
raw_match_spec_str = before + ">=" + version + "," + version_without_last_segment + ".*"
+ build;
}

auto parse_error = [&raw_match_spec_str](std::string_view err) -> tl::unexpected<ParseError>
{
return tl::make_unexpected(ParseError(
Expand Down
5 changes: 4 additions & 1 deletion libmamba/src/specs/version_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,14 @@ fmt::formatter<mamba::specs::VersionPredicate>::format(
}
if constexpr (std::is_same_v<Op, VersionPredicate::compatible_with>)
{
// Make sure to print the version without loosing information.
auto version_level = pred.m_version.version().size();
auto format_level = std::max(op.level, version_level);
out = fmt::format_to(
out,
"{}{}",
VersionSpec::compatible_str,
pred.m_version.str(op.level)
pred.m_version.str(format_level)
);
}
},
Expand Down
219 changes: 212 additions & 7 deletions libmamba/tests/src/specs/test_match_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ namespace
REQUIRE(ms.str() == "abc>3");
}

SECTION("numpy~=1.26.0")
{
auto ms = MatchSpec::parse("numpy~=1.26.0").value();
REQUIRE(ms.name().str() == "numpy");
REQUIRE(ms.version().str() == "~=1.26.0");
REQUIRE(ms.build_string().is_explicitly_free());
REQUIRE(ms.build_number().is_explicitly_free());
REQUIRE(ms.str() == "numpy~=1.26.0");

// TODO: test this assumption for many more cases
auto ms2 = MatchSpec::parse(ms.str()).value();
REQUIRE(ms2 == ms);
}

// Invalid case from `inform2w64-sysroot_win-64-v12.0.0.r2.ggc561118da-h707e725_0.conda`
// which is currently supported but which must not.
SECTION("mingw-w64-ucrt-x86_64-crt-git v12.0.0.r2.ggc561118da h707e725_0")
Expand Down Expand Up @@ -475,28 +489,36 @@ namespace
{
auto ms = MatchSpec::parse(R"(numpy >1.8,<2|==1.7,!=1.9,~=1.7.1 py34_0)").value();
REQUIRE(ms.name().str() == "numpy");
REQUIRE(ms.version().str() == ">1.8,((<2|==1.7),(!=1.9,(>=1.7.1,=1.7)))");
REQUIRE(ms.version().str() == ">1.8,((<2|==1.7),(!=1.9,~=1.7.1))");
REQUIRE(ms.build_string().str() == "py34_0");
REQUIRE(
ms.str()
== R"ms(numpy[version=">1.8,((<2|==1.7),(!=1.9,(>=1.7.1,=1.7)))",build="py34_0"])ms"
ms.str() == R"ms(numpy[version=">1.8,((<2|==1.7),(!=1.9,~=1.7.1))",build="py34_0"])ms"
);
}

SECTION("python-graphviz~=0.20")
{
auto ms = MatchSpec::parse("python-graphviz~=0.20").value();
REQUIRE(ms.name().str() == "python-graphviz");
REQUIRE(ms.version().str() == ">=0.20,=0");
REQUIRE(ms.str() == R"ms(python-graphviz[version=">=0.20,=0"])ms");
REQUIRE(ms.version().str() == "~=0.20");
REQUIRE(ms.str() == R"ms(python-graphviz~=0.20)ms");
}

SECTION("python-graphviz ~= 0.20")
{
auto ms = MatchSpec::parse("python-graphviz ~= 0.20").value();
REQUIRE(ms.name().str() == "python-graphviz");
REQUIRE(ms.version().str() == ">=0.20,=0");
REQUIRE(ms.str() == R"ms(python-graphviz[version=">=0.20,=0"])ms");
REQUIRE(ms.version().str() == "~=0.20");
REQUIRE(ms.str() == R"ms(python-graphviz~=0.20)ms");
}

SECTION("python[version='~=3.11.0',build=*_cpython]")
{
auto ms = MatchSpec::parse("python[version='~=3.11.0',build=*_cpython]").value();
REQUIRE(ms.name().str() == "python");
REQUIRE(ms.version().str() == "~=3.11.0");
REQUIRE(ms.build_string().str() == "*_cpython");
REQUIRE(ms.str() == R"ms(python[version="~=3.11.0",build="*_cpython"])ms");
}

SECTION("*[md5=fewjaflknd]")
Expand Down Expand Up @@ -1000,6 +1022,189 @@ namespace
/* .track_features =*/{},
}));
}

SECTION("pytorch~=2.3.1=py3.10_cuda11.8*")
{
const auto ms = "pytorch~=2.3.1=py3.10_cuda11.8*"_ms;

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "pytorch",
/* .version= */ "2.3.1"_v,
/* .build_string= */ "py3.10_cuda11.8_cudnn8.7.0_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "pytorch",
/* .version= */ "2.3.2"_v,
/* .build_string= */ "py3.10_cuda11.8_cudnn8.7.0_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "pytorch",
/* .version= */ "2.4.0"_v,
/* .build_string= */ "py3.10_cuda11.8_cudnn8.7.0_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "pytorch",
/* .version= */ "3.0"_v,
/* .build_string= */ "py3.10_cuda11.8_cudnn8.7.0_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "pytorch",
/* .version= */ "2.3.0"_v,
/* .build_string= */ "py3.10_cuda11.8_cudnn8.7.0_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));
}

SECTION("numpy~=1.26.0")
{
const auto ms = "numpy~=1.26.0"_ms;

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.26.0"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.26.1"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.27"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "2.0.0"_v,
/* .build_string= */ "py310h1d0b8b9_1",
/* .build_number= */ 1,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.25.0"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));
}

SECTION("numpy~=1.26")
{
const auto ms = "numpy~=1.26"_ms;

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.26.0"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.26.1"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "1.27"_v,
/* .build_string= */ "py310h1d0b8b9_0",
/* .build_number= */ 0,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));

REQUIRE_FALSE(ms.contains_except_channel(Pkg{
/* .name= */ "numpy",
/* .version= */ "2.0.0"_v,
/* .build_string= */ "py310h1d0b8b9_1",
/* .build_number= */ 1,
/* .md5= */ "lemd5",
/* .sha256= */ "somesha256",
/* .license= */ "GPL",
/* .platform= */ "linux-64",
/* .track_features =*/{},
}));
}
}

TEST_CASE("MatchSpec comparability and hashability")
Expand Down
21 changes: 21 additions & 0 deletions libmamba/tests/src/specs/test_version_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,27 @@ namespace
REQUIRE(vs.str() == "=2.3,<3.0");
REQUIRE(vs.str_conda_build() == "2.3.*,<3.0");
}

SECTION("~=1")
{
auto vs = VersionSpec::parse("~=1").value();
REQUIRE(vs.str() == "~=1");
REQUIRE(vs.str_conda_build() == "~=1");
}

SECTION("~=1.8")
{
auto vs = VersionSpec::parse("~=1.8").value();
REQUIRE(vs.str() == "~=1.8");
REQUIRE(vs.str_conda_build() == "~=1.8");
}

SECTION("~=1.8.0")
{
auto vs = VersionSpec::parse("~=1.8.0").value();
REQUIRE(vs.str() == "~=1.8.0");
REQUIRE(vs.str_conda_build() == "~=1.8.0");
}
}

TEST_CASE("VersionSpec::is_explicitly_free")
Expand Down
Loading