|
1 | 1 | use super::clobber_registry::ClobberRegistry;
|
2 |
| -use super::InstallError; |
| 2 | +use super::unlink::{recursively_remove_empty_directories, UnlinkError}; |
| 3 | +use super::{InstallError, Transaction}; |
3 | 4 | use futures::stream::FuturesUnordered;
|
4 | 5 | use futures::{FutureExt, StreamExt};
|
5 |
| -use rattler_conda_types::{PackageRecord, PrefixRecord}; |
| 6 | +use indexmap::IndexSet; |
| 7 | +use itertools::Itertools; |
| 8 | +use rattler_conda_types::prefix_record::PathType; |
| 9 | +use rattler_conda_types::{PackageRecord, PrefixRecord, RepoDataRecord}; |
| 10 | +use std::collections::HashSet; |
6 | 11 | use std::future::pending;
|
7 | 12 | use std::path::Path;
|
8 | 13 | use std::sync::MutexGuard;
|
@@ -152,18 +157,84 @@ impl InstallDriver {
|
152 | 157 | /// and will also execute any `post-link.sh/bat` scripts
|
153 | 158 | pub fn post_process(
|
154 | 159 | &self,
|
155 |
| - prefix_records: &[PrefixRecord], |
| 160 | + transaction: &Transaction<PrefixRecord, RepoDataRecord>, |
156 | 161 | target_prefix: &Path,
|
157 | 162 | ) -> Result<(), InstallError> {
|
| 163 | + let prefix_records = PrefixRecord::collect_from_prefix(target_prefix) |
| 164 | + .map_err(InstallError::PostProcessFailed)?; |
| 165 | + |
158 | 166 | let required_packages =
|
159 | 167 | PackageRecord::sort_topologically(prefix_records.iter().collect::<Vec<_>>());
|
160 | 168 |
|
| 169 | + self.remove_empty_directories(transaction, &prefix_records, target_prefix) |
| 170 | + .unwrap_or_else(|e| { |
| 171 | + tracing::warn!("Failed to remove empty directories: {} (ignored)", e); |
| 172 | + }); |
| 173 | + |
161 | 174 | self.clobber_registry()
|
162 | 175 | .unclobber(&required_packages, target_prefix)
|
163 | 176 | .map_err(InstallError::PostProcessFailed)?;
|
164 | 177 |
|
165 | 178 | Ok(())
|
166 | 179 | }
|
| 180 | + |
| 181 | + /// Remove all empty directories that are not part of the new prefix records. |
| 182 | + pub fn remove_empty_directories( |
| 183 | + &self, |
| 184 | + transaction: &Transaction<PrefixRecord, RepoDataRecord>, |
| 185 | + new_prefix_records: &[PrefixRecord], |
| 186 | + target_prefix: &Path, |
| 187 | + ) -> Result<(), UnlinkError> { |
| 188 | + let mut keep_directories = HashSet::new(); |
| 189 | + |
| 190 | + // find all forced directories in the prefix records |
| 191 | + for record in new_prefix_records { |
| 192 | + for paths in record.paths_data.paths.iter() { |
| 193 | + if paths.path_type == PathType::Directory { |
| 194 | + let path = target_prefix.join(&paths.relative_path); |
| 195 | + keep_directories.insert(path); |
| 196 | + } |
| 197 | + } |
| 198 | + } |
| 199 | + |
| 200 | + // find all removed directories |
| 201 | + for record in transaction.removed_packages() { |
| 202 | + let mut removed_directories = HashSet::new(); |
| 203 | + |
| 204 | + for paths in record.paths_data.paths.iter() { |
| 205 | + if paths.path_type != PathType::Directory { |
| 206 | + if let Some(parent) = paths.relative_path.parent() { |
| 207 | + removed_directories.insert(parent); |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + |
| 212 | + let is_python_noarch = record.repodata_record.package_record.noarch.is_python(); |
| 213 | + |
| 214 | + // Sort the directories by length, so that we delete the deepest directories first. |
| 215 | + let mut directories: IndexSet<_> = removed_directories.into_iter().sorted().collect(); |
| 216 | + |
| 217 | + while let Some(directory) = directories.pop() { |
| 218 | + let directory_path = target_prefix.join(directory); |
| 219 | + let removed_until = recursively_remove_empty_directories( |
| 220 | + &directory_path, |
| 221 | + target_prefix, |
| 222 | + is_python_noarch, |
| 223 | + &keep_directories, |
| 224 | + )?; |
| 225 | + |
| 226 | + // The directory is not empty which means our parent directory is also not empty, |
| 227 | + // recursively remove the parent directory from the set as well. |
| 228 | + while let Some(parent) = removed_until.parent() { |
| 229 | + if !directories.shift_remove(parent) { |
| 230 | + break; |
| 231 | + } |
| 232 | + } |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + Ok(()) |
| 237 | + } |
167 | 238 | }
|
168 | 239 |
|
169 | 240 | impl Drop for InstallDriverInner {
|
|
0 commit comments