Skip to content

Commit b4fcf7b

Browse files
authored
fix: reinstall should rebuild a source package (#4809)
1 parent 3039a71 commit b4fcf7b

File tree

6 files changed

+276
-58
lines changed

6 files changed

+276
-58
lines changed

crates/pixi_cli/src/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {
177177
enabled_protocols: Default::default(),
178178
work_directory: None,
179179
clean: args.clean,
180+
force: false,
180181
build_profile: BuildProfile::Release,
181182
})
182183
.await?;

crates/pixi_command_dispatcher/src/build/build_cache.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,14 +209,14 @@ impl BuildCache {
209209
}
210210

211211
/// Cached result of calling `conda/getMetadata` on a build backend.
212-
#[derive(Debug, Serialize, Deserialize)]
212+
#[derive(Clone, Debug, Serialize, Deserialize)]
213213
pub struct CachedBuild {
214214
#[serde(default, skip_serializing_if = "Option::is_none")]
215215
pub source: Option<CachedBuildSourceInfo>,
216216
pub record: RepoDataRecord,
217217
}
218218

219-
#[derive(Debug, Serialize, Deserialize)]
219+
#[derive(Clone, Debug, Serialize, Deserialize)]
220220
pub struct CachedBuildSourceInfo {
221221
/// The files that were used during the build process. If any of these
222222
/// change, the build should be considered stale.
@@ -235,14 +235,14 @@ pub struct CachedBuildSourceInfo {
235235
}
236236

237237
#[serde_as]
238-
#[derive(Default, Debug, Serialize, Deserialize)]
238+
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
239239
pub struct BuildHostEnvironment {
240240
/// Describes the packages that were installed in the host environment.
241241
pub packages: Vec<BuildHostPackage>,
242242
}
243243

244244
#[serde_as]
245-
#[derive(Debug, Serialize, Deserialize)]
245+
#[derive(Clone, Debug, Serialize, Deserialize)]
246246
pub struct BuildHostPackage {
247247
/// The repodata record of the package.
248248
#[serde(flatten)]

crates/pixi_command_dispatcher/src/install_pixi/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ impl InstallPixiEnvironmentSpec {
206206
source_record: &SourceRecord,
207207
) -> Result<RepoDataRecord, CommandDispatcherError<SourceBuildError>> {
208208
// Build the source package.
209+
// Verify if we need to force the build even if the cache is up to date.
210+
let force = self
211+
.force_reinstall
212+
.contains(&source_record.package_record.name);
209213
let built_source = command_dispatcher
210214
.source_build(SourceBuildSpec {
211215
source: source_record.source.clone(),
@@ -219,6 +223,8 @@ impl InstallPixiEnvironmentSpec {
219223
output_directory: None,
220224
work_directory: None,
221225
clean: false,
226+
// Should we force the build even if the cache is up to date?
227+
force,
222228
// When we install a pixi environment we always build in development mode.
223229
build_profile: BuildProfile::Development,
224230
})

crates/pixi_command_dispatcher/src/source_build/mod.rs

Lines changed: 83 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ pub struct SourceBuildSpec {
9090
/// The protocols that are enabled for this source
9191
#[serde(skip_serializing_if = "crate::is_default")]
9292
pub enabled_protocols: EnabledProtocols,
93+
94+
/// Force rebuild even if the build cache is up to date.
95+
pub force: bool,
9396
}
9497

9598
#[derive(Debug, Clone)]
@@ -127,58 +130,87 @@ impl SourceBuildSpec {
127130
) -> Result<SourceBuildResult, CommandDispatcherError<SourceBuildError>> {
128131
// If the output directory is not set, we want to use the build cache. Read the
129132
// build cache in that case.
130-
let (output_directory, build_cache) =
131-
if let Some(output_directory) = self.output_directory.clone() {
132-
(output_directory, None)
133-
} else {
134-
// Query the source build cache.
135-
let build_cache = command_dispatcher
136-
.source_build_cache_status(SourceBuildCacheStatusSpec {
137-
package: self.package.clone(),
138-
build_environment: self.build_environment.clone(),
139-
source: self.source.clone(),
140-
channels: self.channels.clone(),
141-
channel_config: self.channel_config.clone(),
142-
enabled_protocols: self.enabled_protocols.clone(),
143-
})
144-
.await
145-
.map_err_with(SourceBuildError::from)?;
146-
147-
if let CachedBuildStatus::UpToDate(cached_build) = &build_cache.cached_build {
133+
let (output_directory, build_cache) = if let Some(output_directory) =
134+
self.output_directory.clone()
135+
{
136+
(output_directory, None)
137+
} else {
138+
// Query the source build cache.
139+
let build_cache = command_dispatcher
140+
.source_build_cache_status(SourceBuildCacheStatusSpec {
141+
package: self.package.clone(),
142+
build_environment: self.build_environment.clone(),
143+
source: self.source.clone(),
144+
channels: self.channels.clone(),
145+
channel_config: self.channel_config.clone(),
146+
enabled_protocols: self.enabled_protocols.clone(),
147+
})
148+
.await
149+
.map_err_with(SourceBuildError::from)?;
150+
151+
// If the build is up to date and we are not forcing a rebuild, return the cached build.
152+
// but if we force a rebuild, and we already have an new cached build, we can reuse the cache entry.
153+
if let CachedBuildStatus::UpToDate(cached_build) =
154+
&*build_cache.cached_build.lock().await
155+
{
156+
if !self.force {
148157
// If the build is up to date, we can return the cached build.
149158
tracing::debug!(
150159
source = %self.source,
151160
package = ?cached_build.record.package_record.name,
152161
build = %cached_build.record.package_record.build,
153162
output = %cached_build.record.file_name,
154-
"using cached source build",
163+
"using cached up-to-date source build",
155164
);
156165
return Ok(SourceBuildResult {
157166
output_file: build_cache.cache_dir.join(&cached_build.record.file_name),
158167
record: cached_build.record.clone(),
159168
});
160169
}
170+
tracing::debug!(
171+
"source build for {} is up to date, but force rebuild is set, rebuilding anyway",
172+
self.package.name.as_normalized()
173+
);
174+
}
175+
if let CachedBuildStatus::New(cached_build) = &*build_cache.cached_build.lock().await {
176+
tracing::debug!(
177+
"source build for {} is already built and marked as new, reusing the cache entry",
178+
self.package.name.as_normalized()
179+
);
180+
tracing::debug!(
181+
source = %self.source,
182+
package = ?cached_build.record.package_record.name,
183+
build = %cached_build.record.package_record.build,
184+
output = %cached_build.record.file_name,
185+
"using cached new source build",
186+
);
187+
// dont matter if we forceit , we can reuse the cache entry
188+
return Ok(SourceBuildResult {
189+
output_file: build_cache.cache_dir.join(&cached_build.record.file_name),
190+
record: cached_build.record.clone(),
191+
});
192+
}
161193

162-
match &build_cache.cached_build {
163-
CachedBuildStatus::Stale(existing) => {
164-
tracing::debug!(
165-
source = %self.source,
166-
package = ?existing.record.package_record.name,
167-
build = %existing.record.package_record.build,
168-
"rebuilding stale source build",
169-
);
170-
}
171-
CachedBuildStatus::Missing => {
172-
tracing::debug!(
173-
source = %self.source,
174-
"no cached source build; starting fresh build",
175-
);
176-
}
177-
CachedBuildStatus::UpToDate(_) => {}
194+
match &*build_cache.cached_build.lock().await {
195+
CachedBuildStatus::Stale(existing) => {
196+
tracing::debug!(
197+
source = %self.source,
198+
package = ?existing.record.package_record.name,
199+
build = %existing.record.package_record.build,
200+
"rebuilding stale source build",
201+
);
202+
}
203+
CachedBuildStatus::Missing => {
204+
tracing::debug!(
205+
source = %self.source,
206+
"no cached source build; starting fresh build",
207+
);
178208
}
209+
CachedBuildStatus::UpToDate(_) | CachedBuildStatus::New(_) => {}
210+
}
179211

180-
(build_cache.cache_dir.clone(), Some(build_cache))
181-
};
212+
(build_cache.cache_dir.clone(), Some(build_cache))
213+
};
182214

183215
// Check out the source code.
184216
let source_checkout = command_dispatcher
@@ -335,14 +367,21 @@ impl SourceBuildSpec {
335367
// Update the cache entry if we have one.
336368
if let Some(build_cache) = build_cache {
337369
let mut entry = build_cache.entry.lock().await;
370+
// set the status that its a new cache
371+
// so on the next run we can distinguish between up to date ( was already saved from previous session)
372+
// and new that was just build now
373+
let cached_build = CachedBuild {
374+
source: source_checkout
375+
.pinned
376+
.is_mutable()
377+
.then_some(built_source.metadata.clone()),
378+
record: record.clone(),
379+
};
380+
381+
let mut cached_entry = build_cache.cached_build.lock().await;
382+
*cached_entry = CachedBuildStatus::New(cached_build.clone());
338383
entry
339-
.insert(CachedBuild {
340-
source: source_checkout
341-
.pinned
342-
.is_mutable()
343-
.then_some(built_source.metadata),
344-
record: record.clone(),
345-
})
384+
.insert(cached_build)
346385
.await
347386
.map_err(SourceBuildError::BuildCache)
348387
.map_err(CommandDispatcherError::Failed)?;

crates/pixi_command_dispatcher/src/source_build_cache_status/mod.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,25 @@ pub struct SourceBuildCacheStatusSpec {
5050
pub enabled_protocols: EnabledProtocols,
5151
}
5252

53+
#[derive(Debug)]
5354
pub enum CachedBuildStatus {
5455
/// The build was found in the cache but is stale.
5556
Stale(CachedBuild),
5657

57-
/// The build was found in the cache and is up to date.
58+
/// The build was found in the cache from previous session and is up to date.
5859
UpToDate(CachedBuild),
5960

61+
/// The build was build during the running session.
62+
New(CachedBuild),
63+
6064
/// The build was not found in the cache.
6165
Missing,
6266
}
6367

6468
pub struct SourceBuildCacheEntry {
6569
/// The information stored in the build cache. Or `None` if the build did
6670
/// not exist in the cache.
67-
pub cached_build: CachedBuildStatus,
71+
pub cached_build: Mutex<CachedBuildStatus>,
6872

6973
/// A reference to the build entry in the cache. Not that as long as this
7074
/// entry exists a lock is retained on the cache entry.
@@ -100,6 +104,10 @@ impl SourceBuildCacheStatusSpec {
100104
.map_err(CommandDispatcherError::Failed)?;
101105

102106
// Check the staleness of the cached entry
107+
tracing::debug!(
108+
"determining cache status for package '{}' from source build cache",
109+
self.package,
110+
);
103111
let cached_build = match cached_build {
104112
Some(cached_build) => {
105113
self.determine_cache_status(&command_dispatcher, cached_build)
@@ -108,8 +116,14 @@ impl SourceBuildCacheStatusSpec {
108116
None => CachedBuildStatus::Missing,
109117
};
110118

119+
tracing::debug!(
120+
"status of cached build for package '{}' is '{:?}'",
121+
self.package,
122+
&cached_build
123+
);
124+
111125
Ok(SourceBuildCacheEntry {
112-
cached_build,
126+
cached_build: Mutex::new(cached_build),
113127
cache_dir: build_cache_entry.cache_dir().to_path_buf(),
114128
entry: Mutex::new(build_cache_entry),
115129
})
@@ -134,7 +148,9 @@ impl SourceBuildCacheStatusSpec {
134148
.check_package_configuration_changed(command_dispatcher, cached_build, source)
135149
.await?
136150
{
137-
CachedBuildStatus::UpToDate(cached_build) => cached_build,
151+
CachedBuildStatus::UpToDate(cached_build) | CachedBuildStatus::New(cached_build) => {
152+
cached_build
153+
}
138154
CachedBuildStatus::Stale(cached_build) => {
139155
return Ok(CachedBuildStatus::Stale(cached_build));
140156
}
@@ -146,7 +162,9 @@ impl SourceBuildCacheStatusSpec {
146162
.check_source_out_of_date(command_dispatcher, cached_build, source)
147163
.await?
148164
{
149-
CachedBuildStatus::UpToDate(cached_build) => cached_build,
165+
CachedBuildStatus::UpToDate(cached_build) | CachedBuildStatus::New(cached_build) => {
166+
cached_build
167+
}
150168
CachedBuildStatus::Stale(cached_build) => {
151169
return Ok(CachedBuildStatus::Stale(cached_build));
152170
}
@@ -205,14 +223,15 @@ impl SourceBuildCacheStatusSpec {
205223
return Err(CommandDispatcherError::Failed(err));
206224
}
207225
Ok(entry) => {
208-
match &entry.cached_build {
226+
match &*entry.cached_build.lock().await {
209227
CachedBuildStatus::Missing | CachedBuildStatus::Stale(_) => {
210228
tracing::debug!(
211229
"package is stale because its build dependency '{identifier}' is missing or stale",
212230
);
213231
return Ok(CachedBuildStatus::Stale(cached_build));
214232
}
215-
CachedBuildStatus::UpToDate(dependency_cached_build) => {
233+
CachedBuildStatus::UpToDate(dependency_cached_build)
234+
| CachedBuildStatus::New(dependency_cached_build) => {
216235
// Is this version of the package also what we expect?
217236
//
218237
// Maybe the package that we previously used was actually updated

0 commit comments

Comments
 (0)