Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

propose a repodata patch function to be able to add pip to the `pyt… #238

Merged
merged 11 commits into from
Jul 7, 2023
28 changes: 24 additions & 4 deletions crates/rattler-bin/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use rattler::{
package_cache::PackageCache,
};
use rattler_conda_types::{
Channel, ChannelConfig, GenericVirtualPackage, MatchSpec, Platform, PrefixRecord,
RepoDataRecord, Version,
Channel, ChannelConfig, GenericVirtualPackage, MatchSpec, PackageRecord, Platform,
PrefixRecord, RepoDataRecord, Version,
};
use rattler_networking::{AuthenticatedClient, AuthenticationStorage};
use rattler_repodata_gateway::fetch::{
Expand Down Expand Up @@ -158,7 +158,15 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> {
// Get the package names from the matchspecs so we can only load the package records that we need.
let package_names = specs.iter().filter_map(|spec| spec.name.as_ref());
let repodatas = wrap_in_progress("parsing repodata", move || {
SparseRepoData::load_records_recursive(&sparse_repo_datas, package_names)
SparseRepoData::load_records_recursive(
&sparse_repo_datas,
package_names,
Some(|record| {
if record.name == "python" {
record.depends.push("pip".to_string());
}
}),
)
})?;

// Determine virtual packages of the system. These packages define the capabilities of the
Expand Down Expand Up @@ -220,6 +228,9 @@ pub async fn create(opt: Opt) -> anyhow::Result<()> {
}
})?;

// sort topologically
let required_packages = PackageRecord::sort_topologically(required_packages);

// Construct a transaction to
let transaction = Transaction::from_current_and_desired(
installed_packages,
Expand Down Expand Up @@ -604,7 +615,16 @@ async fn fetch_repo_data_records_with_progress(
// task.
let repo_data_json_path = result.repo_data_json_path.clone();
match tokio::task::spawn_blocking(move || {
SparseRepoData::new(channel, platform.to_string(), repo_data_json_path)
SparseRepoData::new(
channel,
platform.to_string(),
repo_data_json_path,
Some(|record: &mut PackageRecord| {
if record.name == "python" {
record.depends.push("pip".to_string());
}
}),
)
})
.await
{
Expand Down
76 changes: 44 additions & 32 deletions crates/rattler/src/install/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::collections::HashSet;

use crate::install::python::PythonInfoError;
use crate::install::PythonInfo;
use rattler_conda_types::{PackageRecord, Platform};
use std::collections::HashMap;

#[derive(Debug, thiserror::Error)]
pub enum TransactionError {
Expand Down Expand Up @@ -86,49 +87,60 @@ impl<Old: AsRef<PackageRecord>, New: AsRef<PackageRecord>> Transaction<Old, New>
CurIter::IntoIter: Clone,
NewIter::IntoIter: Clone,
{
let current = current.into_iter();
let desired = desired.into_iter();
let current_iter = current.into_iter();
let desired_iter = desired.into_iter();

// Determine the python version used in the current situation.
let current_python_info = find_python_info(current.clone(), platform)?;
let desired_python_info = find_python_info(desired.clone(), platform)?;
let current_python_info = find_python_info(current_iter.clone(), platform)?;
let desired_python_info = find_python_info(desired_iter.clone(), platform)?;
let needs_python_relink = match (&current_python_info, &desired_python_info) {
(Some(current), Some(desired)) => desired.is_relink_required(current),
_ => false,
};

// Create a lookup table by name for the desired packages.
let mut desired: HashMap<String, New> = desired
.into_iter()
.map(|record| (record.as_ref().name.clone(), record))
.collect();

let mut operations = Vec::new();

// Find all the elements that are no longer in the desired set
for record in current {
match desired.remove(&record.as_ref().name) {
None => operations.push(TransactionOperation::Remove(record)),
Some(desired) => {
// If the desired differs from the current it has to be updated.
if !describe_same_content(desired.as_ref(), record.as_ref()) {
operations.push(TransactionOperation::Change {
old: record,
new: desired,
})
}
// If this is a noarch package and all python packages need to be relinked,
// reinstall the package completely.
else if desired.as_ref().noarch.is_python() && needs_python_relink {
operations.push(TransactionOperation::Reinstall(record));
}
}
let mut current_map = current_iter
.clone()
.map(|r| (r.as_ref().name.clone(), r))
.collect::<std::collections::HashMap<_, _>>();

let desired_names = desired_iter
.clone()
.map(|r| r.as_ref().name.clone())
.collect::<HashSet<_>>();

// Remove all current packages that are not in desired (but keep order of current)
for record in current_iter {
if !desired_names.contains(&record.as_ref().name) {
operations.push(TransactionOperation::Remove(record));
}
}

// The remaining packages from the desired list need to be explicitly installed.
for record in desired.into_values() {
operations.push(TransactionOperation::Install(record))
// reverse all removals, last in first out
operations.reverse();

// Figure out the operations to perform, but keep the order of the original "desired" iterator
for record in desired_iter {
let name = &record.as_ref().name;
let old_record = current_map.remove(name);

if let Some(old_record) = old_record {
if !describe_same_content(record.as_ref(), old_record.as_ref()) {
// if the content changed, we need to reinstall (remove and install)
operations.push(TransactionOperation::Change {
old: old_record,
new: record,
});
} else if needs_python_relink {
// when the python version changed, we need to relink all noarch packages
// to recompile the bytecode
operations.push(TransactionOperation::Reinstall(old_record));
}
// if the content is the same, we dont need to do anything
} else {
operations.push(TransactionOperation::Install(record));
}
}

Ok(Self {
Expand Down
2 changes: 1 addition & 1 deletion crates/rattler_conda_types/src/repo_data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ impl PackageRecord {
/// the order of `records` and of the `depends` vector inside the records.
///
/// Note that this function only works for packages with unique names.
pub fn sort_topologically<T: AsRef<PackageRecord>>(records: Vec<T>) -> Vec<T> {
pub fn sort_topologically<T: AsRef<PackageRecord> + Clone>(records: Vec<T>) -> Vec<T> {
topological_sort::sort_topologically(records)
}
}
Expand Down
Loading