Skip to content

Commit

Permalink
Feature/target specific tasks (#269)
Browse files Browse the repository at this point in the history
* feat: add the target specific task to the manifest so it can be read.
* feat: add target specific tasks retrieval to the project.
* feat: Add the target specific tasks to the cli
* feat: add platforms to the init for the tests (and the users)
* feat: target specific running, adding and deleting of tasks
* fix: pixi info show all available commands like pixi task list
  • Loading branch information
ruben-arts authored Aug 22, 2023
1 parent 4a135eb commit 2905605
Show file tree
Hide file tree
Showing 14 changed files with 461 additions and 77 deletions.
6 changes: 5 additions & 1 deletion src/cli/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,11 @@ pub async fn execute(args: Args) -> miette::Result<()> {

let project_info = project.map(|p| ProjectInfo {
manifest_path: p.root().to_path_buf().join("pixi.toml"),
tasks: p.manifest.tasks.keys().cloned().collect(),
tasks: p
.tasks(Some(Platform::current()))
.into_keys()
.map(|k| k.to_string())
.collect(),
package_count: dependency_count(&p).ok(),
environment_size,
last_updated: last_updated(p.lock_file_path()).ok(),
Expand Down
14 changes: 11 additions & 3 deletions src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub struct Args {
/// Channels to use in the project.
#[arg(short, long = "channel", id = "channel")]
pub channels: Option<Vec<String>>,

/// Platforms that the project supports.
#[arg(short, long = "platform", id = "platform")]
pub platforms: Vec<String>,
}

/// The default channels to use for a new project.
Expand All @@ -35,7 +39,7 @@ description = "Add a short description here"
authors = ["{{ author[0] }} <{{ author[1] }}>"]
{%- endif %}
channels = [{%- if channels %}"{{ channels|join("\", \"") }}"{%- endif %}]
platforms = ["{{ platform }}"]
platforms = ["{{ platforms|join("\", \"") }}"]
[tasks]
Expand Down Expand Up @@ -90,7 +94,11 @@ pub async fn execute(args: Args) -> miette::Result<()> {
.collect()
};

let platform = Platform::current();
let platforms = if args.platforms.is_empty() {
vec![Platform::current().to_string()]
} else {
args.platforms
};

let rv = env
.render_named_str(
Expand All @@ -101,7 +109,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {
version,
author,
channels,
platform
platforms
},
)
.unwrap();
Expand Down
44 changes: 32 additions & 12 deletions src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,35 @@ pub fn order_tasks(
) -> miette::Result<VecDeque<(Task, Vec<String>)>> {
let tasks: Vec<_> = tasks.iter().map(|c| c.to_string()).collect();

// Find the command in the project.
// Find the command in the tasks.
let (task_name, task, additional_args) = tasks
.first()
// First search in the target specific tasks
.and_then(|cmd_name| {
project.task_opt(cmd_name).map(|cmd| {
(
Some(cmd_name.clone()),
cmd.clone(),
tasks[1..].iter().cloned().collect_vec(),
)
project
.target_specific_tasks(Platform::current())
.get(cmd_name.as_str())
.map(|&cmd| {
(
Some(cmd_name.clone()),
cmd.clone(),
tasks[1..].iter().cloned().collect_vec(),
)
})
})
// If it isn't found in the target specific tasks try to find it in the default tasks.
.or_else(|| {
tasks.first().and_then(|cmd_name| {
project.task_opt(cmd_name).map(|cmd| {
(
Some(cmd_name.clone()),
cmd.clone(),
tasks[1..].iter().cloned().collect_vec(),
)
})
})
})
// When no task is found, just execute the command.
.unwrap_or_else(|| {
(
None,
Expand Down Expand Up @@ -93,11 +110,14 @@ pub fn order_tasks(
for dependency in depends_on.iter() {
if !added.contains(dependency) {
let cmd = project
.task_opt(dependency)
.ok_or_else(|| miette::miette!("failed to find dependency {}", dependency))?
.clone();

s1.push_back((cmd, Vec::new()));
.target_specific_tasks(Platform::current())
.get(dependency.as_str())
.copied()
// If there is no target specific task try to find it in the default tasks.
.or_else(|| project.task_opt(dependency))
.ok_or_else(|| miette::miette!("failed to find dependency {}", dependency))?;

s1.push_back((cmd.clone(), Vec::new()));
added.insert(dependency.clone());
}
}
Expand Down
55 changes: 41 additions & 14 deletions src/cli/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::task::{Alias, CmdArgs, Execute, Task};
use crate::Project;
use clap::Parser;
use itertools::Itertools;
use rattler_conda_types::Platform;
use std::path::PathBuf;

#[derive(Parser, Debug)]
Expand All @@ -28,9 +29,13 @@ pub enum Operation {
pub struct RemoveArgs {
/// Task names to remove
pub names: Vec<String>,

/// The platform for which the task should be removed
#[arg(long, short)]
pub platform: Option<Platform>,
}

#[derive(Parser, Debug)]
#[derive(Parser, Debug, Clone)]
#[clap(arg_required_else_help = true)]
pub struct AddArgs {
/// Task name
Expand All @@ -44,9 +49,13 @@ pub struct AddArgs {
#[clap(long)]
#[clap(num_args = 1..)]
pub depends_on: Option<Vec<String>>,

/// The platform for which the task should be added
#[arg(long, short)]
pub platform: Option<Platform>,
}

#[derive(Parser, Debug)]
#[derive(Parser, Debug, Clone)]
#[clap(arg_required_else_help = true)]
pub struct AliasArgs {
/// Alias name
Expand All @@ -55,6 +64,10 @@ pub struct AliasArgs {
/// Depends on these tasks to execute
#[clap(required = true, num_args = 1..)]
pub depends_on: Vec<String>,

/// The platform for which the alias should be added
#[arg(long, short)]
pub platform: Option<Platform>,
}

impl From<AddArgs> for Task {
Expand Down Expand Up @@ -105,9 +118,9 @@ pub fn execute(args: Args) -> miette::Result<()> {
let mut project = Project::load_or_else_discover(args.manifest_path.as_deref())?;
match args.operation {
Operation::Add(args) => {
let name = args.name.clone();
let task: Task = args.into();
project.add_task(&name, task.clone())?;
let name = &args.name;
let task: Task = args.clone().into();
project.add_task(name, task.clone(), args.platform)?;
eprintln!(
"{}Added task {}: {}",
console::style(console::Emoji("✔ ", "+")).green(),
Expand All @@ -118,16 +131,30 @@ pub fn execute(args: Args) -> miette::Result<()> {
Operation::Remove(args) => {
let mut to_remove = Vec::new();
for name in args.names.iter() {
if project.task_opt(name).is_none() {
if let Some(platform) = args.platform {
if !project
.target_specific_tasks(platform)
.contains_key(name.as_str())
{
eprintln!(
"{}Task '{}' does not exist on {}",
console::style(console::Emoji("❌ ", "X")).red(),
console::style(&name).bold(),
console::style(platform.as_str()).bold(),
);
continue;
}
} else if !project.tasks(None).contains_key(name.as_str()) {
eprintln!(
"{}Task {} does not exist",
console::style(console::Emoji("❌ ", "X")).red(),
console::style(&name).bold(),
);
continue;
}

// Check if task has dependencies
let depends_on = project.task_depends_on(name);
let depends_on = project.task_names_depending_on(name);
if !depends_on.is_empty() && !args.names.contains(name) {
eprintln!(
"{}: {}",
Expand All @@ -141,11 +168,11 @@ pub fn execute(args: Args) -> miette::Result<()> {
);
}
// Safe to remove
to_remove.push(name);
to_remove.push((name, args.platform));
}

for name in to_remove {
project.remove_task(name)?;
for (name, platform) in to_remove {
project.remove_task(name, platform)?;
eprintln!(
"{}Removed task {} ",
console::style(console::Emoji("❌ ", "X")).yellow(),
Expand All @@ -154,9 +181,9 @@ pub fn execute(args: Args) -> miette::Result<()> {
}
}
Operation::Alias(args) => {
let name = args.alias.clone();
let task: Task = args.into();
project.add_task(&name, task.clone())?;
let name = &args.alias;
let task: Task = args.clone().into();
project.add_task(name, task.clone(), args.platform)?;
eprintln!(
"{} Added alias {}: {}",
console::style("@").blue(),
Expand All @@ -165,7 +192,7 @@ pub fn execute(args: Args) -> miette::Result<()> {
);
}
Operation::List => {
let tasks = project.tasks();
let tasks = project.task_names(Some(Platform::current()));
if tasks.is_empty() {
eprintln!("No tasks found",);
} else {
Expand Down
25 changes: 25 additions & 0 deletions src/project/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ pub struct TargetMetadata {
/// Additional information to activate an environment.
#[serde(default)]
pub activation: Option<Activation>,

/// Target specific tasks to run in the environment
#[serde(default)]
pub tasks: HashMap<String, Task>,
}

/// Describes the contents of the `[package]` section of the project manifest.
Expand Down Expand Up @@ -456,4 +460,25 @@ mod test {
toml_edit::de::from_str::<ProjectManifest>(&contents).expect("parsing should succeed!")
);
}

#[test]
fn test_target_specific_tasks() {
let contents = format!(
r#"
{PROJECT_BOILERPLATE}
[tasks]
test = "test multi"
[target.win-64.tasks]
test = "test win"
[target.linux-64.tasks]
test = "test linux"
"#
);

assert_debug_snapshot!(
toml_edit::de::from_str::<ProjectManifest>(&contents).expect("parsing should succeed!")
);
}
}
Loading

0 comments on commit 2905605

Please sign in to comment.