Skip to content
Draft
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
108 changes: 82 additions & 26 deletions libmamba/src/api/create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
//
// The full license is in the file LICENSE, distributed with this software.

#include <fstream>
#include <iostream>

#include <yaml-cpp/yaml.h>

#include "mamba/api/configuration.hpp"
#include "mamba/api/create.hpp"
#include "mamba/api/install.hpp"
Expand Down Expand Up @@ -57,6 +60,7 @@ namespace mamba
void clone_environment(
Context& ctx,
ChannelContext& channel_context,
Configuration& config,
const fs::u8path& source_prefix,
bool create_env,
bool remove_prefix_on_failure
Expand All @@ -77,40 +81,85 @@ namespace mamba
}
const PrefixData& source_prefix_data = maybe_prefix_data.value();

std::vector<std::string> explicit_urls;
const auto records = source_prefix_data.sorted_records();
explicit_urls.reserve(records.size());

for (const auto& pkg : records)
// Export source environment to YAML format
TemporaryFile yaml_file("mamba_env_", ".yml");
{
if (pkg.package_url.empty())
YAML::Emitter out;
out << YAML::BeginMap;

// Add dependencies section
out << YAML::Key << "dependencies" << YAML::Value << YAML::BeginSeq;

// Add conda packages
const auto records = source_prefix_data.sorted_records();
for (const auto& pkg : records)
{
// Fallback to channel/platform/filename if possible.
if (pkg.channel.empty() || pkg.platform.empty() || pkg.filename.empty())
{
LOG_WARNING << "Skipping package without URL information while cloning: "
<< pkg.name;
continue;
}
const auto url = pkg.url_for_channel(pkg.channel);
explicit_urls.push_back(url);
out << fmt::format("{}={}={}", pkg.name, pkg.version, pkg.build_string);
}
else

// Add pip packages as a sub-map
const auto& pip_records = source_prefix_data.pip_records();
if (!pip_records.empty())
{
std::string url = pkg.package_url;
if (!pkg.sha256.empty())
out << YAML::BeginMap;
out << YAML::Key << "pip" << YAML::Value << YAML::BeginSeq;
for (const auto& [name, pkg] : pip_records)
{
url += "#sha256:" + pkg.sha256;
out << (pkg.name + "==" + pkg.version);
}
else if (!pkg.md5.empty())
{
url += "#" + pkg.md5;
}
explicit_urls.push_back(std::move(url));
out << YAML::EndSeq;
out << YAML::EndMap;
}

out << YAML::EndSeq;
out << YAML::EndMap;

// Write YAML to temporary file
std::ofstream yaml_out = open_ofstream(yaml_file.path());
yaml_out << out.c_str();
yaml_out.close();
}

install_explicit_specs(ctx, channel_context, explicit_urls, create_env, remove_prefix_on_failure);
// Read YAML file and populate config
const auto parse_result = detail::read_yaml_file(
ctx,
yaml_file.path().string(),
ctx.platform,
ctx.use_uv
);

// Populate config with parsed YAML contents
if (!parse_result.dependencies.empty())
{
auto& specs = config.at("specs").value<std::vector<std::string>>();
specs.insert(
specs.end(),
parse_result.dependencies.begin(),
parse_result.dependencies.end()
);
}

if (!parse_result.others_pkg_mgrs_specs.empty())
{
auto& others_pkg_mgrs_specs = config.at("others_pkg_mgrs_specs")
.value<std::vector<detail::other_pkg_mgr_spec>>();
others_pkg_mgrs_specs.insert(
others_pkg_mgrs_specs.end(),
parse_result.others_pkg_mgrs_specs.begin(),
parse_result.others_pkg_mgrs_specs.end()
);
}

// Install packages from config
const auto& install_specs_vec = config.at("specs").value<std::vector<std::string>>();
Comment on lines +153 to +154
Copy link
Member Author

Choose a reason for hiding this comment

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

This is inspired from one of the install path, but we must not install those specifications modifying the configuration…

install_specs(
ctx,
channel_context,
config,
install_specs_vec,
create_env,
remove_prefix_on_failure
);
}
} // namespace

Expand Down Expand Up @@ -239,7 +288,14 @@ namespace mamba
{
const auto clone_value = clone_cfg.value<std::string>();
const auto source_prefix = compute_clone_source_prefix(ctx, clone_value);
clone_environment(ctx, channel_context, source_prefix, create_env, remove_prefix_on_failure);
clone_environment(
ctx,
channel_context,
config,
source_prefix,
create_env,
remove_prefix_on_failure
);
return;
}

Expand Down
18 changes: 13 additions & 5 deletions libmamba/src/api/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ namespace mamba
{
tl::expected<command_args, std::runtime_error> get_pkg_mgr_install_command(
const std::string& name,
const std::string& target_prefix,
const fs::u8path& prefix,
const fs::u8path& spec_file,
pip::Update update
)
{
const auto get_python_path = [&]
{ return util::which_in("python", util::get_path_dirs(target_prefix)).string(); };
{ return util::which_in("python", util::get_path_dirs(prefix)).string(); };

const auto get_uv_path = [&]
{ return util::which_in("uv", util::get_path_dirs(target_prefix)).string(); };
{ return util::which_in("uv", util::get_path_dirs(prefix)).string(); };

command_args cmd = [&]
{
Expand Down Expand Up @@ -157,14 +157,21 @@ namespace mamba
{
specs_f << d.c_str() << '\n';
}
specs_f.flush();
specs_f.close();
}

// Use target_prefix to find pip/python executable
const fs::u8path& target_prefix = ctx.prefix_params.target_prefix;
// Ensure we use an absolute path for the requirements file
const fs::u8path specs_path = fs::absolute(specs.path());

command_args command = [&]
{
const auto maybe_command = get_pkg_mgr_install_command(
pkg_mgr,
ctx.prefix_params.target_prefix.string(),
specs.path(),
target_prefix,
specs_path,
update
);
if (maybe_command)
Expand Down Expand Up @@ -198,6 +205,7 @@ namespace mamba
fmt::print(LOG_INFO, "Calling: {}", fmt::join(command, " "));

auto [status, ec] = reproc::run(wrapped_command, options);

assert_reproc_success(options, status, ec);
if (status != 0)
{
Expand Down
Loading
Loading