Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: project model using targets, features and environments #616

Merged
merged 7 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
372 changes: 190 additions & 182 deletions Cargo.lock

Large diffs are not rendered by default.

19 changes: 9 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,16 @@ tokio = { version = "1.34.0", features = ["rt"] }
toml = "0.8.8"

[patch.crates-io]
#rattler = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_conda_types = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_digest = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_lock = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_networking = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_repodata_gateway = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_shell = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_solve = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_virtual_packages = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_conda_types = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_digest = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_lock = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_networking = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_repodata_gateway = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_shell = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_solve = { git = "https://github.com/mamba-org/rattler", branch = "main" }
rattler_virtual_packages = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rip = { package = "rattler_installs_packages", git = "https://github.com/prefix-dev/rattler_installs_packages", branch = "main" }

#deno_task_shell = { path = "../deno_task_shell" }

#rattler = { path = "../rattler/crates/rattler" }
Expand Down
12 changes: 5 additions & 7 deletions src/cli/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,12 @@ pub async fn add_pypi_specs_to_project(
// TODO: Get best version
// Add the dependency to the project
if specs_platforms.is_empty() {
project.manifest.add_pypi_dependency(name, spec)?;
project.manifest.add_pypi_dependency(name, spec, None)?;
} else {
for platform in specs_platforms.iter() {
project
.manifest
.add_target_pypi_dependency(*platform, name.clone(), spec)?;
.add_pypi_dependency(name, spec, Some(*platform))?;
}
}
}
Expand Down Expand Up @@ -268,9 +268,7 @@ pub async fn add_conda_specs_to_project(
// SpecType::Build => project.build_dependencies(platform)?,
// SpecType::Run => project.dependencies(platform)?,
// };
let mut current_specs = project.dependencies(platform)?;
current_specs.extend(project.host_dependencies(platform)?);
current_specs.extend(project.build_dependencies(platform)?);
let current_specs = project.all_dependencies(platform);

// Solve the environment with the new specs added
let solved_versions = match determine_best_version(
Expand Down Expand Up @@ -312,12 +310,12 @@ pub async fn add_conda_specs_to_project(

// Add the dependency to the project
if specs_platforms.is_empty() {
project.manifest.add_dependency(&spec, spec_type)?;
project.manifest.add_dependency(&spec, spec_type, None)?;
} else {
for platform in specs_platforms.iter() {
project
.manifest
.add_target_dependency(*platform, &spec, spec_type)?;
.add_dependency(&spec, spec_type, Some(*platform))?;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ fn last_updated(path: impl Into<PathBuf>) -> miette::Result<String> {
/// Returns number of dependencies on current platform
fn dependency_count(project: &Project) -> miette::Result<u64> {
let dep_count = project
.all_dependencies(Platform::current())?
.all_dependencies(Platform::current())
.keys()
.cloned()
.fold(0, |acc, _| acc + 1);
Expand Down
12 changes: 4 additions & 8 deletions src/cli/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,9 @@ pub async fn execute(args: Args) -> miette::Result<()> {
let results = deps
.iter()
.map(|dep| {
if let Some(p) = &args.platform {
project
.manifest
.remove_target_dependency(dep, &spec_type, p)
} else {
project.manifest.remove_dependency(dep, &spec_type)
}
project
.manifest
.remove_dependency(dep, spec_type, args.platform)
})
.collect::<Vec<_>>();

Expand All @@ -68,7 +64,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {

eprintln!(
"Removed {} from [{}]",
console::style(format!("{removed} {spec}")).bold(),
console::style(format!("{} {spec}", removed.as_source())).bold(),
console::style(table_name).bold(),
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::task::{
};
use crate::{
environment::get_up_to_date_prefix, prefix::Prefix, progress::await_in_progress,
project::environment::get_metadata_env, Project,
project::metadata::get_metadata_env, Project,
};
use rattler_shell::{
activation::{ActivationVariables, Activator, PathModificationBehavior},
Expand Down
2 changes: 1 addition & 1 deletion src/cli/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::unix::PtySession;
use crate::cli::LockFileUsageArgs;
use crate::environment::get_up_to_date_prefix;
use crate::environment::LockFileUsage;
use crate::project::environment::get_metadata_env;
use crate::project::metadata::get_metadata_env;
#[cfg(target_family = "windows")]
use rattler_shell::shell::CmdExe;

Expand Down
44 changes: 43 additions & 1 deletion src/cli/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use clap::Parser;
use itertools::Itertools;
use rattler_conda_types::Platform;
use std::path::PathBuf;
use toml_edit::{Array, Item, Table, Value};

#[derive(Parser, Debug)]
pub enum Operation {
Expand Down Expand Up @@ -142,6 +143,7 @@ pub fn execute(args: Args) -> miette::Result<()> {
project
.manifest
.add_task(name, task.clone(), args.platform)?;
project.save()?;
eprintln!(
"{}Added task {}: {}",
console::style(console::Emoji("✔ ", "+")).green(),
Expand All @@ -155,7 +157,7 @@ pub fn execute(args: Args) -> miette::Result<()> {
if let Some(platform) = args.platform {
if !project
.manifest
.target_specific_tasks(platform)
.tasks(Some(platform))
.contains_key(name.as_str())
{
eprintln!(
Expand Down Expand Up @@ -195,6 +197,7 @@ pub fn execute(args: Args) -> miette::Result<()> {

for (name, platform) in to_remove {
project.manifest.remove_task(name, platform)?;
project.save()?;
eprintln!(
"{}Removed task {} ",
console::style(console::Emoji("✔ ", "+")).green(),
Expand All @@ -208,6 +211,7 @@ pub fn execute(args: Args) -> miette::Result<()> {
project
.manifest
.add_task(name, task.clone(), args.platform)?;
project.save()?;
eprintln!(
"{} Added alias {}: {}",
console::style("@").blue(),
Expand Down Expand Up @@ -239,3 +243,41 @@ pub fn execute(args: Args) -> miette::Result<()> {

Ok(())
}

impl From<Task> for Item {
fn from(value: Task) -> Self {
match value {
Task::Plain(str) => Item::Value(str.into()),
Task::Execute(process) => {
let mut table = Table::new().into_inline_table();
match process.cmd {
CmdArgs::Single(cmd_str) => {
table.insert("cmd", cmd_str.into());
}
CmdArgs::Multiple(cmd_strs) => {
table.insert("cmd", Value::Array(Array::from_iter(cmd_strs)));
}
}
if !process.depends_on.is_empty() {
table.insert(
"depends_on",
Value::Array(Array::from_iter(process.depends_on)),
);
}
if let Some(cwd) = process.cwd {
table.insert("cwd", cwd.to_string_lossy().to_string().into());
}
Item::Value(Value::InlineTable(table))
}
Task::Alias(alias) => {
let mut table = Table::new().into_inline_table();
table.insert(
"depends_on",
Value::Array(Array::from_iter(alias.depends_on)),
);
Item::Value(Value::InlineTable(table))
}
_ => Item::None,
}
}
}
2 changes: 1 addition & 1 deletion src/lock_file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ async fn resolve_platform(
platform: Platform,
pb: ProgressBar,
) -> miette::Result<LockedPackagesBuilder> {
let dependencies = project.all_dependencies(platform)?;
let dependencies = project.all_dependencies(platform);
let match_specs = dependencies
.iter()
.map(|(name, constraint)| MatchSpec::from_nameless(constraint.clone(), Some(name.clone())))
Expand Down
4 changes: 2 additions & 2 deletions src/lock_file/satisfiability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ pub fn lock_file_satisfies_project(
for platform in platforms.iter().cloned() {
// Check if all dependencies exist in the lock-file.
let conda_dependencies = project
.all_dependencies(platform)?
.all_dependencies(platform)
.into_iter()
.map(|(name, spec)| DependencyKind::Conda(MatchSpec::from_nameless(spec, Some(name))))
.collect::<Vec<_>>();

let mut pypi_dependencies = project
.pypi_dependencies(platform)
.into_iter()
.map(|(name, requirement)| requirement.as_pep508(name))
.map(|(name, requirement)| requirement.as_pep508(&name))
.map(DependencyKind::PyPi)
.peekable();

Expand Down
38 changes: 38 additions & 0 deletions src/project/manifest/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::utils::spanned::PixiSpanned;

/// The name of an environment. This is either a string or default for the default environment.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum EnvironmentName {
Default,
Named(String),
}

impl EnvironmentName {
/// Returns the name of the environment or `None` if this is the default environment.
pub fn name(&self) -> Option<&str> {
match self {
EnvironmentName::Default => None,
EnvironmentName::Named(name) => Some(name),
}
}
}

/// An environment describes a set of features that are available together.
///
/// Individual features cannot be used directly, instead they are grouped together into
/// environments. Environments are then locked and installed.
#[derive(Debug, Clone)]
pub struct Environment {
/// The name of the environment
pub name: EnvironmentName,

/// The names of the features that together make up this environment.
///
/// Note that the default feature is always added to the set of features that make up the
/// environment.
pub features: PixiSpanned<Vec<String>>,

/// An optional solver-group. Multiple environments can share the same solve-group. All the
/// dependencies of the environment that share the same solve-group will be solved together.
pub solve_group: Option<String>,
}
62 changes: 62 additions & 0 deletions src/project/manifest/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::project::manifest::{FeatureName, TargetSelector};
use crate::project::SpecType;
use miette::Diagnostic;
use thiserror::Error;

/// An error that is returned when a certain spec is missing.
#[derive(Debug, Error, Diagnostic)]
#[error("{name} is missing")]
pub struct SpecIsMissing {
// The name of the dependency that is missing,
pub name: String,

// The type of the dependency that is missing.
pub spec_type: SpecType,

/// Whether the dependency itself is missing or the entire dependency spec type is missing
pub spec_type_is_missing: bool,

// The target from which the dependency is missing.
pub target: Option<TargetSelector>,

// The feature from which the dependency is missing.
pub feature: Option<FeatureName>,
}

impl SpecIsMissing {
/// Constructs a new `SpecIsMissing` error that indicates that a spec type is missing.
///
/// This is constructed for instance when the `[build-dependencies]` section is missing.
pub fn spec_type_is_missing(name: impl Into<String>, spec_type: SpecType) -> Self {
Self {
name: name.into(),
spec_type,
spec_type_is_missing: true,
target: None,
feature: None,
}
}

/// Constructs a new `SpecIsMissing` error that indicates that a spec is missing
pub fn dep_is_missing(name: impl Into<String>, spec_type: SpecType) -> Self {
Self {
name: name.into(),
spec_type,
spec_type_is_missing: false,
target: None,
feature: None,
}
}

/// Set the target from which the spec is missing.
pub fn with_target(mut self, target: TargetSelector) -> Self {
self.target = Some(target);
self
}

/// Sets the feature from which the spec is missing.
pub fn with_feature(mut self, feature: FeatureName) -> Self {
self.feature = Some(feature);
self
}
}
66 changes: 66 additions & 0 deletions src/project/manifest/feature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use super::SystemRequirements;
use crate::project::manifest::target::Targets;
use crate::utils::spanned::PixiSpanned;
use rattler_conda_types::{Channel, Platform};
use serde::de::Error;
use serde::Deserialize;

/// The name of a feature. This is either a string or default for the default feature.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum FeatureName {
Default,
Named(String),
}

impl<'de> Deserialize<'de> for FeatureName {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match String::deserialize(deserializer)?.as_str() {
"default" => Err(D::Error::custom(
"The name 'default' is reserved for the default feature",
)),
name => Ok(FeatureName::Named(name.to_string())),
}
}
}

impl FeatureName {
/// Returns the name of the feature or `None` if this is the default feature.
pub fn name(&self) -> Option<&str> {
match self {
FeatureName::Default => None,
FeatureName::Named(name) => Some(name),
}
}
}

/// A feature describes a set of functionalities. It allows us to group functionality and its
/// dependencies together.
///
/// Individual features cannot be used directly, instead they are grouped together into
/// environments. Environments are then locked and installed.
#[derive(Debug, Clone)]
pub struct Feature {
/// The name of the feature or `None` if the feature is the default feature.
pub name: FeatureName,

/// The platforms this feature is available on.
///
/// This value is `None` if this feature does not specify any platforms and the default
/// platforms from the project should be used.
pub platforms: Option<PixiSpanned<Vec<Platform>>>,

/// Channels specific to this feature.
///
/// This value is `None` if this feature does not specify any channels and the default
/// channels from the project should be used.
pub channels: Option<Vec<Channel>>,

/// Additional system requirements
pub system_requirements: SystemRequirements,

/// Target specific configuration.
pub targets: Targets,
}
Loading
Loading