diff --git a/crates/rattler_lock/src/conda.rs b/crates/rattler_lock/src/conda.rs index c382834ff..acff11715 100644 --- a/crates/rattler_lock/src/conda.rs +++ b/crates/rattler_lock/src/conda.rs @@ -157,12 +157,16 @@ pub enum GitShallowSpec { Rev, } -/// Package build source kind. +/// Package build source location for reproducible builds. +/// +/// This stores the exact source location information needed to +/// reproducibly build a package from source. Used by pixi build +/// and other package building tools. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum PackageBuildSourceKind { - /// Git repository source with specific revision +pub enum PackageBuildSource { + /// Git repository source with specific revision. Git { - /// The repository URL + /// The repository URL. url: Url, /// Shallow specification of repository head to use. /// @@ -170,37 +174,23 @@ pub enum PackageBuildSourceKind { spec: Option, /// The specific git revision. rev: String, + /// Subdirectory on which focus on. + subdir: Option, }, - /// URL-based archive source with content hash + /// URL-based archive source with content hash. Url { - /// The URL to the archive + /// The URL to the archive. url: Url, - /// The SHA256 hash of the archive content + /// The SHA256 hash of the archive content. sha256: Sha256Hash, + /// Subdirectory on which focus on. + subdir: Option, + }, + /// Source is some local path. + Path { + /// Actual path. + path: Utf8TypedPathBuf, }, -} - -/// Package build source location for reproducible builds. -/// -/// This stores the exact source location information needed to -/// reproducibly build a package from source. Used by pixi build -/// and other package building tools. -/// -/// There are 3 different types of locations: path, git, url -/// (archive). We store only git and url sources, since path-based -/// sources can change over time and would require expensive -/// computation of directory file hashes for reproducibility. -/// -/// For git sources we store the repository url and exact revision. -/// For url sources we store the archive url and its content hash. -/// -/// For both kinds we store subdirectory. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct PackageBuildSource { - /// Kind of source we fetch. - pub kind: PackageBuildSourceKind, - /// Subdirectory of the source from which build starts. - pub subdirectory: Option, } /// Information about a source package stored in the lock-file. diff --git a/crates/rattler_lock/src/lib.rs b/crates/rattler_lock/src/lib.rs index 8b485aeb5..24da720f9 100644 --- a/crates/rattler_lock/src/lib.rs +++ b/crates/rattler_lock/src/lib.rs @@ -99,7 +99,7 @@ pub use builder::{LockFileBuilder, LockedPackage}; pub use channel::Channel; pub use conda::{ CondaBinaryData, CondaPackageData, CondaSourceData, ConversionError, GitShallowSpec, InputHash, - PackageBuildSource, PackageBuildSourceKind, + PackageBuildSource, }; pub use file_format_version::FileFormatVersion; pub use hash::PackageHashes; diff --git a/crates/rattler_lock/src/parse/models/v6/source_data.rs b/crates/rattler_lock/src/parse/models/v6/source_data.rs index 2c8bdedcb..19afcf118 100644 --- a/crates/rattler_lock/src/parse/models/v6/source_data.rs +++ b/crates/rattler_lock/src/parse/models/v6/source_data.rs @@ -5,7 +5,7 @@ use serde_with::{serde_as, DeserializeAs, SerializeAs}; use typed_path::Utf8TypedPathBuf; use url::Url; -use crate::conda::{GitShallowSpec, PackageBuildSource, PackageBuildSourceKind}; +use crate::conda::{GitShallowSpec, PackageBuildSource}; use crate::source::{ GitReference, GitSourceLocation, PathSourceLocation, SourceLocation, UrlSourceLocation, }; @@ -114,6 +114,9 @@ pub enum SourceLocationError { #[error("must specify none or exactly one of `branch`, `tag` or `rev`")] MultipleGitReferences, + + #[error("`path` can not have `subdir`")] + PathSubdir, } impl<'a> TryFrom> for SourceLocation { @@ -144,6 +147,9 @@ impl<'a> TryFrom> for SourceLocation { let url = url.into_owned(); Ok(SourceLocation::Url(UrlSourceLocation { url, md5, sha256 })) } else if let Some(path) = path { + if subdirectory.is_some() { + return Err(SourceLocationError::PathSubdir); + } let path = path.into_owned().into(); Ok(SourceLocation::Path(PathSourceLocation { path })) } else if let Some(git) = git { @@ -187,14 +193,22 @@ struct PackageBuildSourceData<'a> { #[serde(skip_serializing_if = "Option::is_none")] pub rev: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub subdir: Option>, } impl<'a> From<&'a PackageBuildSource> for PackageBuildSourceData<'a> { fn from(value: &'a PackageBuildSource) -> Self { - match &value.kind { - PackageBuildSourceKind::Git { url, spec, rev } => { + match value { + PackageBuildSource::Git { + url, + spec, + rev, + subdir, + } => { let (branch, tag, explicit_rev) = match spec { Some(GitShallowSpec::Branch(branch)) => { (Some(Cow::Borrowed(branch.as_str())), None, None) @@ -205,10 +219,7 @@ impl<'a> From<&'a PackageBuildSource> for PackageBuildSourceData<'a> { Some(GitShallowSpec::Rev) => (None, None, Some(true)), None => (None, None, None), }; - let subdir = value - .subdirectory - .as_ref() - .map(|p| Cow::Borrowed(p.as_str())); + let subdir = subdir.as_ref().map(|p| Cow::Borrowed(p.as_str())); Self { url: None, sha256: None, @@ -216,11 +227,16 @@ impl<'a> From<&'a PackageBuildSource> for PackageBuildSourceData<'a> { branch, tag, explicit_rev, - subdir, + path: None, rev: Some(Cow::Borrowed(rev)), + subdir, } } - PackageBuildSourceKind::Url { url, sha256 } => Self { + PackageBuildSource::Url { + url, + sha256, + subdir, + } => Self { url: Some(Cow::Borrowed(url)), sha256: Some(*sha256), git: None, @@ -228,6 +244,18 @@ impl<'a> From<&'a PackageBuildSource> for PackageBuildSourceData<'a> { tag: None, explicit_rev: None, rev: None, + path: None, + subdir: subdir.as_ref().map(|p| Cow::Borrowed(p.as_str())), + }, + PackageBuildSource::Path { path } => Self { + url: None, + sha256: None, + git: None, + branch: None, + tag: None, + explicit_rev: None, + rev: None, + path: Some(Cow::Borrowed(path.as_str())), subdir: None, }, } @@ -261,10 +289,11 @@ impl<'a> TryFrom> for PackageBuildSource { tag, rev, explicit_rev, + path, subdir, } = value; - let count = [url.is_some(), git.is_some()] + let count = [url.is_some(), git.is_some(), path.is_some()] .into_iter() .filter(|&x| x) .count(); @@ -272,14 +301,16 @@ impl<'a> TryFrom> for PackageBuildSource { return Err(PackageBuildSourceError::MissingOrMultipleSourceRoots); } - let subdirectory = subdir.map(|s| Utf8TypedPathBuf::from(&*s)); + let path = path.map(|s| Utf8TypedPathBuf::from(&*s)); + let subdir = subdir.map(|s| Utf8TypedPathBuf::from(&*s)); if let Some(url) = url { let url = url.into_owned(); let sha256 = sha256.ok_or(PackageBuildSourceError::MissingSha256ForUrl)?; - Ok(PackageBuildSource { - kind: PackageBuildSourceKind::Url { url, sha256 }, - subdirectory, + Ok(PackageBuildSource::Url { + url, + sha256, + subdir, }) } else if let Some(git) = git { let git = git.into_owned(); @@ -301,14 +332,14 @@ impl<'a> TryFrom> for PackageBuildSource { None }; - Ok(PackageBuildSource { - kind: PackageBuildSourceKind::Git { - url: git, - spec, - rev, - }, - subdirectory, + Ok(PackageBuildSource::Git { + url: git, + spec, + rev, + subdir, }) + } else if let Some(path) = path { + Ok(PackageBuildSource::Path { path }) } else { unreachable!("we already checked that exactly one of url or git is set") }