Skip to content

Commit 96a779b

Browse files
committed
also take into account noarch python files when clobbering
1 parent 38ae450 commit 96a779b

9 files changed

+226
-54
lines changed

crates/rattler/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ chrono = { workspace = true }
2323
digest = { workspace = true }
2424
dirs = { workspace = true }
2525
drop_bomb = { workspace = true }
26+
fs-err = "2.11.0"
2627
futures = { workspace = true }
2728
fxhash = { workspace = true }
2829
hex = { workspace = true }

crates/rattler/src/install/clobber_registry.rs

+146-18
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22
33
use std::{
44
collections::HashMap,
5-
fs,
65
path::{Path, PathBuf},
76
};
87

8+
use fs_err as fs;
9+
910
use drop_bomb::DropBomb;
10-
use rattler_conda_types::{package::PathsJson, PackageName, PrefixRecord};
11+
use rattler_conda_types::{
12+
package::{IndexJson, PathsEntry},
13+
PackageName, PrefixRecord,
14+
};
1115

1216
/// A registry for clobbering files
1317
/// The registry keeps track of all files that are installed by a package and
1418
/// can be used to rename files that are already installed by another package.
15-
#[derive(Debug)]
1619
pub struct ClobberRegistry {
1720
paths_registry: HashMap<PathBuf, usize>,
1821
clobbers: HashMap<PathBuf, Vec<usize>>,
@@ -103,10 +106,11 @@ impl ClobberRegistry {
103106
/// will "unclobber" the files after all packages have been installed.
104107
pub fn register_paths(
105108
&mut self,
106-
name: &PackageName,
107-
paths_json: &PathsJson,
109+
index_json: &IndexJson,
110+
computed_paths: &Vec<(PathsEntry, PathBuf)>,
108111
) -> HashMap<PathBuf, PathBuf> {
109112
let mut clobber_paths = HashMap::new();
113+
let name = &index_json.name.clone();
110114

111115
// check if we have the package name already registered
112116
let name_idx = if let Some(idx) = self.package_names.iter().position(|n| n == name) {
@@ -116,25 +120,51 @@ impl ClobberRegistry {
116120
self.package_names.len() - 1
117121
};
118122

119-
for entry in paths_json.paths.iter() {
120-
let path = entry.relative_path.clone();
121-
123+
// let entry_points = if index_json.noarch.is_python() {
124+
// let mut res = Vec::new();
125+
// if let (Some(link_json), Some(python_info)) = (link_json, python_info) {
126+
// // register entry points
127+
// if let NoArchLinks::Python(entry_points) = &link_json.noarch {
128+
// for entry_point in &entry_points.entry_points {
129+
// if python_info.platform.is_windows() {
130+
// let relative_path_script_py = python_info
131+
// .bin_dir
132+
// .join(format!("{}-script.py", &entry_point.command));
133+
// let relative_path_script_exe = python_info
134+
// .bin_dir
135+
// .join(format!("{}.exe", &entry_point.command));
136+
// res.push(relative_path_script_py);
137+
// res.push(relative_path_script_exe);
138+
// } else {
139+
// let file = python_info.bin_dir.join(&entry_point.command);
140+
// res.push(file);
141+
// }
142+
// }
143+
// }
144+
// }
145+
// res
146+
// } else {
147+
// Vec::new()
148+
// };
149+
150+
for (_, path) in computed_paths {
122151
// if we find an entry, we have a clobbering path!
123-
if let Some(e) = self.paths_registry.get(&path) {
152+
if let Some(e) = self.paths_registry.get(path) {
124153
if e == &name_idx {
125154
// A name cannot appear twice in an environment.
126155
// We get into this case if a package is updated (removed and installed again with a new version)
127156
continue;
128157
}
129-
let new_path = Self::clobber_name(&path, &self.package_names[name_idx]);
158+
let new_path = Self::clobber_name(path, &self.package_names[name_idx]);
130159
self.clobbers
131160
.entry(path.clone())
132161
.or_insert_with(|| vec![*e])
133162
.push(name_idx);
134163

135-
clobber_paths.insert(path, new_path);
164+
// We insert the non-renamed path here
165+
clobber_paths.insert(path.clone(), new_path);
136166
} else {
137-
self.paths_registry.insert(path, name_idx);
167+
self.paths_registry.insert(path.clone(), name_idx);
138168
}
139169
}
140170

@@ -170,7 +200,7 @@ impl ClobberRegistry {
170200
let winner = sorted_clobbered_by.last().expect("No winner found");
171201

172202
if winner.1 == clobbered_by_names[0] {
173-
tracing::debug!(
203+
tracing::info!(
174204
"clobbering decision: keep {} from {:?}",
175205
path.display(),
176206
winner
@@ -181,7 +211,10 @@ impl ClobberRegistry {
181211
let loser_name = &clobbered_by_names[0];
182212
let loser_path = Self::clobber_name(path, loser_name);
183213

184-
fs::rename(target_prefix.join(path), target_prefix.join(&loser_path))?;
214+
if let Err(e) = fs::rename(target_prefix.join(path), target_prefix.join(&loser_path)) {
215+
tracing::info!("could not rename file: {}", e);
216+
continue;
217+
}
185218

186219
let loser_idx = sorted_clobbered_by
187220
.iter()
@@ -196,7 +229,7 @@ impl ClobberRegistry {
196229
true,
197230
);
198231

199-
tracing::debug!(
232+
tracing::info!(
200233
"clobbering decision: remove {} from {:?}",
201234
path.display(),
202235
loser_name
@@ -208,13 +241,13 @@ impl ClobberRegistry {
208241

209242
let winner_path = Self::clobber_name(path, &winner.1);
210243

211-
tracing::debug!(
244+
tracing::info!(
212245
"clobbering decision: choose {} from {:?}",
213246
path.display(),
214247
winner
215248
);
216249

217-
std::fs::rename(target_prefix.join(&winner_path), target_prefix.join(path))?;
250+
fs::rename(target_prefix.join(&winner_path), target_prefix.join(path))?;
218251

219252
let winner_prefix_record = rename_path_in_prefix_record(
220253
sorted_prefix_records[winner.0],
@@ -275,12 +308,14 @@ mod tests {
275308
use std::{
276309
fs,
277310
path::{Path, PathBuf},
311+
str::FromStr,
278312
};
279313

280314
use futures::TryFutureExt;
281315
use rand::seq::SliceRandom;
282316
use rattler_conda_types::{
283317
package::IndexJson, PackageRecord, Platform, PrefixRecord, RepoDataRecord,
318+
Version,
284319
};
285320
use rattler_digest::{Md5, Sha256};
286321
use rattler_networking::retry_policies::default_retry_policy;
@@ -289,12 +324,13 @@ mod tests {
289324

290325
use crate::{
291326
get_test_data_dir,
292-
install::{transaction, unlink_package, InstallDriver, InstallOptions},
327+
install::{transaction, unlink_package, InstallDriver, InstallOptions, PythonInfo},
293328
package_cache::PackageCache,
294329
};
295330

296331
fn get_repodata_record(filename: &str) -> RepoDataRecord {
297332
let path = fs::canonicalize(get_test_data_dir().join(filename)).unwrap();
333+
print!("{:?}", path);
298334
let index_json = read_package_file::<IndexJson>(&path).unwrap();
299335

300336
// find size and hash
@@ -465,6 +501,18 @@ mod tests {
465501
]
466502
}
467503

504+
fn test_python_noarch_operations() -> Vec<TransactionOperation<PrefixRecord, RepoDataRecord>> {
505+
let repodata_record_1 =
506+
get_repodata_record("clobber/clobber-pynoarch-1-0.1.0-pyh4616a5c_0.tar.bz2");
507+
let repodata_record_2 =
508+
get_repodata_record("clobber/clobber-pynoarch-2-0.1.0-pyh4616a5c_0.tar.bz2");
509+
510+
vec![
511+
TransactionOperation::Install(repodata_record_1),
512+
TransactionOperation::Install(repodata_record_2),
513+
]
514+
}
515+
468516
fn test_operations_nested() -> Vec<TransactionOperation<PrefixRecord, RepoDataRecord>> {
469517
let repodata_record_1 =
470518
get_repodata_record("clobber/clobber-nested-1-0.1.0-h4616a5c_0.tar.bz2");
@@ -502,6 +550,8 @@ mod tests {
502550
.collect::<Vec<_>>();
503551

504552
assert_eq!(files.len(), expected_files.len());
553+
println!("{:?}", files);
554+
505555
for file in files {
506556
assert!(expected_files.contains(&file.file_name().unwrap().to_string_lossy().as_ref()));
507557
}
@@ -929,6 +979,7 @@ mod tests {
929979
platform: Platform::current(),
930980
};
931981

982+
let prefix_records = PrefixRecord::collect_from_prefix(target_prefix.path()).unwrap();
932983
let install_driver = InstallDriver::new(100, Some(&prefix_records));
933984

934985
execute_transaction(
@@ -948,5 +999,82 @@ mod tests {
948999
fs::read_to_string(target_prefix.path().join("clobber.txt")).unwrap(),
9491000
"clobber-3 v2\n"
9501001
);
1002+
1003+
let update_ops = test_operations_update();
1004+
1005+
// remove one of the clobbering files
1006+
let transaction = transaction::Transaction::<PrefixRecord, RepoDataRecord> {
1007+
operations: vec![TransactionOperation::Install(update_ops[0].clone())],
1008+
python_info: None,
1009+
current_python_info: None,
1010+
platform: Platform::current(),
1011+
};
1012+
1013+
let prefix_records = PrefixRecord::collect_from_prefix(target_prefix.path()).unwrap();
1014+
let install_driver = InstallDriver::new(100, Some(&prefix_records));
1015+
1016+
execute_transaction(
1017+
transaction,
1018+
target_prefix.path(),
1019+
&reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new()),
1020+
&cache,
1021+
&install_driver,
1022+
&InstallOptions::default(),
1023+
)
1024+
.await;
1025+
1026+
assert_check_files(
1027+
target_prefix.path(),
1028+
&["clobber.txt", "clobber.txt__clobber-from-clobber-3"],
1029+
);
1030+
1031+
// content of clobber.txt
1032+
assert_eq!(
1033+
fs::read_to_string(target_prefix.path().join("clobber.txt")).unwrap(),
1034+
"clobber-1 v2\n"
1035+
);
1036+
}
1037+
1038+
#[tokio::test]
1039+
async fn test_clobber_python_noarch() {
1040+
// Create a transaction
1041+
let operations = test_python_noarch_operations();
1042+
1043+
let python_info =
1044+
PythonInfo::from_version(&Version::from_str("3.11.0").unwrap(), Platform::current())
1045+
.unwrap();
1046+
let transaction = transaction::Transaction::<PrefixRecord, RepoDataRecord> {
1047+
operations,
1048+
python_info: Some(python_info.clone()),
1049+
current_python_info: Some(python_info.clone()),
1050+
platform: Platform::current(),
1051+
};
1052+
1053+
// execute transaction
1054+
let target_prefix = tempfile::tempdir().unwrap();
1055+
1056+
let packages_dir = tempfile::tempdir().unwrap();
1057+
let cache = PackageCache::new(packages_dir.path());
1058+
1059+
let mut install_options = InstallOptions::default();
1060+
install_options.python_info = Some(python_info.clone());
1061+
1062+
execute_transaction(
1063+
transaction,
1064+
target_prefix.path(),
1065+
&reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new()),
1066+
&cache,
1067+
&InstallDriver::default(),
1068+
&install_options,
1069+
)
1070+
.await;
1071+
1072+
// check that the files are there
1073+
assert_check_files(
1074+
&target_prefix
1075+
.path()
1076+
.join("lib/python3.11/site-packages/clobber"),
1077+
&["clobber.py", "clobber.py__clobber-from-clobber-pynoarch-2"],
1078+
);
9511079
}
9521080
}

crates/rattler/src/install/driver.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ impl InstallDriver {
160160

161161
self.clobber_registry()
162162
.unclobber(&required_packages, target_prefix)
163-
.map_err(InstallError::PostProcessFailed)?;
163+
.map_err(|e| {
164+
tracing::error!("Error unclobbering packages: {:?}", e);
165+
InstallError::PostProcessFailed(e)
166+
})?;
164167

165168
Ok(())
166169
}

crates/rattler/src/install/link.rs

+4-26
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
//! This module contains the logic to link a give file from the package cache into the target directory.
22
//! See [`link_file`] for more information.
3-
use crate::install::python::PythonInfo;
43
use memmap2::Mmap;
54
use rattler_conda_types::package::{FileMode, PathType, PathsEntry, PrefixPlaceholder};
6-
use rattler_conda_types::{NoArchType, Platform};
5+
use rattler_conda_types::Platform;
76
use rattler_digest::HashingWriter;
87
use rattler_digest::Sha256;
98
use reflink_copy::reflink;
@@ -131,47 +130,26 @@ pub struct LinkedFile {
131130
/// [`crate::install::InstallOptions::target_prefix`] for more information.
132131
#[allow(clippy::too_many_arguments)] // TODO: Fix this properly
133132
pub fn link_file(
134-
noarch_type: NoArchType,
135133
path_json_entry: &PathsEntry,
134+
destination_relative_path: PathBuf,
136135
package_dir: &Path,
137136
target_dir: &Path,
138137
target_prefix: &str,
139138
allow_symbolic_links: bool,
140139
allow_hard_links: bool,
141140
allow_ref_links: bool,
142141
target_platform: Platform,
143-
target_python: Option<&PythonInfo>,
144142
apple_codesign_behavior: AppleCodeSignBehavior,
145-
clobber_rename: Option<&PathBuf>,
146143
) -> Result<LinkedFile, LinkFileError> {
147144
let source_path = package_dir.join(&path_json_entry.relative_path);
148145

149-
// Determine the destination path
150-
let destination_relative_path = if noarch_type.is_python() {
151-
match target_python {
152-
Some(python_info) => {
153-
python_info.get_python_noarch_target_path(&path_json_entry.relative_path)
154-
}
155-
None => return Err(LinkFileError::MissingPythonInfo),
156-
}
157-
} else if let Some(clobber_rename) = clobber_rename {
158-
clobber_rename.into()
159-
} else {
160-
path_json_entry.relative_path.as_path().into()
161-
};
162-
163146
let destination_path = target_dir.join(&destination_relative_path);
164147

165148
// Ensure that all directories up to the path exist.
166149
if let Some(parent) = destination_path.parent() {
167150
std::fs::create_dir_all(parent).map_err(LinkFileError::FailedToCreateParentDirectory)?;
168151
}
169152

170-
// If the file already exists it most likely means that the file is clobbered. This means that
171-
// different packages are writing to the same file. This function simply reports back to the
172-
// caller that this is the case but there is no special handling here.
173-
let clobbered = clobber_rename.is_some();
174-
175153
// Temporary variables to store intermediate computations in. If we already computed the file
176154
// size or the sha hash we dont have to recompute them at the end of the function.
177155
let mut sha256 = None;
@@ -300,10 +278,10 @@ pub fn link_file(
300278
};
301279

302280
Ok(LinkedFile {
303-
clobbered,
281+
clobbered: false,
304282
sha256,
305283
file_size,
306-
relative_path: destination_relative_path.into_owned(),
284+
relative_path: destination_relative_path,
307285
method: link_method,
308286
})
309287
}

0 commit comments

Comments
 (0)