Skip to content

Commit 69d5001

Browse files
authored
fix: better out-of source caching and source code propagation (#4875)
1 parent 68c71bc commit 69d5001

File tree

39 files changed

+1855
-431
lines changed

39 files changed

+1855
-431
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pixi_build_discovery/src/backend_spec.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use pixi_spec::{BinarySpec, PixiSpec, SourceAnchor};
1+
use pixi_spec::{BinarySpec, PixiSpec, SourceAnchor, SourceSpec};
22
use pixi_spec_containers::DependencyMap;
33
use rattler_conda_types::ChannelUrl;
44
/// Describes how a backend should be instantiated.
@@ -45,8 +45,10 @@ impl JsonRpcBackendSpec {
4545
let maybe_source_spec = env_spec.requirement.1.try_into_source_spec();
4646
let pixi_spec = match maybe_source_spec {
4747
Ok(source_spec) => {
48-
let resolved_spec = source_anchor.resolve(source_spec);
49-
PixiSpec::from(resolved_spec)
48+
let resolved_spec = source_anchor.resolve(source_spec.location);
49+
PixiSpec::from(SourceSpec {
50+
location: resolved_spec,
51+
})
5052
}
5153
Err(pixi_spec) => pixi_spec,
5254
};
@@ -133,8 +135,10 @@ impl EnvironmentSpec {
133135
let maybe_source_spec = self.requirement.1.try_into_source_spec();
134136
let pixi_spec = match maybe_source_spec {
135137
Ok(source_spec) => {
136-
let resolved_spec = source_anchor.resolve(source_spec);
137-
PixiSpec::from(resolved_spec)
138+
let resolved_spec = source_anchor.resolve(source_spec.location);
139+
PixiSpec::from(SourceSpec {
140+
location: resolved_spec,
141+
})
138142
}
139143
Err(pixi_spec) => pixi_spec,
140144
};

crates/pixi_build_types/src/procedures/initialize.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ pub const METHOD_NAME: &str = "initialize";
2727
pub struct InitializeParams {
2828
/// The manifest that the build backend should use.
2929
///
30-
/// This is an absolute path.
30+
/// This is an absolute path to a manifest file.
3131
pub manifest_path: PathBuf,
3232

3333
/// The root directory of the source code that the build backend should use.
3434
/// If this is `None`, the backend should use the directory of the
3535
/// `manifest_path` as the source directory.
3636
///
37-
/// This is an absolute path.
37+
/// This is an absolute path. This is always a directory.
3838
pub source_dir: Option<PathBuf>,
3939

4040
/// The root directory of the workspace.

crates/pixi_cli/src/build.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use miette::{Context, IntoDiagnostic};
77
use pixi_build_frontend::BackendOverride;
88
use pixi_command_dispatcher::{
99
BuildBackendMetadataSpec, BuildEnvironment, BuildProfile, CacheDirs, SourceBuildSpec,
10+
build::SourceCodeLocation,
1011
};
1112
use pixi_config::ConfigCli;
1213
use pixi_consts::consts::{
@@ -270,13 +271,13 @@ pub async fn execute(args: Args) -> miette::Result<()> {
270271
// Create the build backend metadata specification.
271272
let backend_metadata_spec = BuildBackendMetadataSpec {
272273
manifest_source: manifest_source.clone(),
274+
preferred_build_source: None,
273275
channels: channels.clone(),
274276
channel_config: channel_config.clone(),
275277
build_environment: build_environment.clone(),
276278
variants: Some(variants.clone()),
277279
variant_files: Some(variant_files.clone()),
278280
enabled_protocols: Default::default(),
279-
pin_override: None,
280281
};
281282
let backend_metadata = command_dispatcher
282283
.build_backend_metadata(backend_metadata_spec.clone())
@@ -307,8 +308,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {
307308
package,
308309
// Build into a temporary directory first
309310
output_directory: Some(temp_output_dir.path().to_path_buf()),
310-
manifest_source: manifest_source.clone(),
311-
build_source: None,
311+
source: SourceCodeLocation::new(manifest_source.clone(), None),
312312
channels: channels.clone(),
313313
channel_config: channel_config.clone(),
314314
build_environment: build_environment.clone(),

crates/pixi_command_dispatcher/src/build/build_cache.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use thiserror::Error;
1919
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
2020
use xxhash_rust::xxh3::Xxh3;
2121

22-
use crate::build::source_checkout_cache_key;
22+
use crate::build::{SourceCodeLocation, source_checkout_cache_key};
2323

2424
/// A cache for caching build artifacts of a source checkout.
2525
#[derive(Clone)]
@@ -249,7 +249,7 @@ pub struct BuildHostPackage {
249249
pub repodata_record: RepoDataRecord,
250250

251251
/// The source location from which the package was built.
252-
pub source: Option<PinnedSourceSpec>,
252+
pub source: Option<SourceCodeLocation>,
253253
}
254254

255255
/// A cache entry returned by [`BuildCache::entry`] which enables

crates/pixi_command_dispatcher/src/build/dependencies.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use pixi_build_types::{
88
},
99
};
1010
use pixi_record::PixiRecord;
11-
use pixi_spec::{BinarySpec, DetailedSpec, PixiSpec, SourceAnchor, UrlBinarySpec};
11+
use pixi_spec::{BinarySpec, DetailedSpec, PixiSpec, SourceAnchor, SourceSpec, UrlBinarySpec};
1212
use pixi_spec_containers::DependencyMap;
1313
use rattler_conda_types::{
1414
InvalidPackageNameError, MatchSpec, NamedChannelOrUrl, NamelessMatchSpec, PackageName,
@@ -95,12 +95,12 @@ impl Dependencies {
9595
})?;
9696
match conversion::from_package_spec_v1(depend.spec.clone()).into_source_or_binary() {
9797
Either::Left(source) => {
98-
let source = if let Some(anchor) = &source_anchor {
99-
anchor.resolve(source)
98+
let location = if let Some(anchor) = &source_anchor {
99+
anchor.resolve(source.location)
100100
} else {
101-
source
101+
source.location
102102
};
103-
dependencies.insert(name, PixiSpec::from(source).into());
103+
dependencies.insert(name, PixiSpec::from(SourceSpec { location }).into());
104104
}
105105
Either::Right(binary) => {
106106
dependencies.insert(name, PixiSpec::from(binary).into());

crates/pixi_command_dispatcher/src/build/mod.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,79 @@ pub use dependencies::{
2222
};
2323
pub(crate) use move_file::{MoveError, move_file};
2424
use pixi_record::PinnedSourceSpec;
25+
use serde::{Deserialize, Serialize};
2526
use url::Url;
2627
pub use work_dir_key::{SourceRecordOrCheckout, WorkDirKey};
2728
use xxhash_rust::xxh3::Xxh3;
2829

2930
const KNOWN_SUFFIXES: [&str; 3] = [".git", ".tar.gz", ".zip"];
3031

32+
/// Stores the two possible locations for the source code,
33+
/// in the case of an out-of-tree source build.
34+
///
35+
/// Something which looks like:
36+
/// ```toml
37+
/// [package.build]
38+
/// source = { path = "some-path" }
39+
/// ```
40+
///
41+
/// We want to prefer that location for our cache checks
42+
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
43+
pub struct SourceCodeLocation {
44+
/// The location of the manifest and the possible source code
45+
manifest_source: PinnedSourceSpec,
46+
/// The location of the source code that should be queried and build
47+
build_source: Option<PinnedSourceSpec>,
48+
}
49+
50+
impl SourceCodeLocation {
51+
pub fn new(manifest_source: PinnedSourceSpec, build_source: Option<PinnedSourceSpec>) -> Self {
52+
Self {
53+
manifest_source,
54+
build_source,
55+
}
56+
}
57+
58+
/// Get the reference to the manifest source
59+
pub fn manifest_source(&self) -> &PinnedSourceSpec {
60+
&self.manifest_source
61+
}
62+
63+
/// Get the pinned source spec to the actual source code
64+
/// This is the normally the path to the manifest_source
65+
/// but when set is the path to the build_source
66+
pub fn source_code(&self) -> &PinnedSourceSpec {
67+
self.build_source.as_ref().unwrap_or(&self.manifest_source)
68+
}
69+
70+
/// Get the optional explicit build source override.
71+
pub fn build_source(&self) -> Option<&PinnedSourceSpec> {
72+
self.build_source.as_ref()
73+
}
74+
75+
pub fn as_source_and_alternative_root(&self) -> (&PinnedSourceSpec, Option<&PinnedSourceSpec>) {
76+
if let Some(build_source) = &self.build_source {
77+
(build_source, Some(&self.manifest_source))
78+
} else {
79+
(&self.manifest_source, None)
80+
}
81+
}
82+
}
83+
84+
impl std::fmt::Display for SourceCodeLocation {
85+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86+
write!(
87+
f,
88+
"(manifest-src: {}, build-src: {})",
89+
self.manifest_source(),
90+
self.build_source
91+
.as_ref()
92+
.map(|build| format!("{build}"))
93+
.unwrap_or("undefined".to_string())
94+
)
95+
}
96+
}
97+
3198
/// Try to deduce a name from a url.
3299
fn pretty_url_name(url: &Url) -> String {
33100
if let Some(last_segment) = url

0 commit comments

Comments
 (0)