Idea for a crate/tool: `cargo-task`


#1

Hi!

I find myself in a need of a simple cli tool when working on my Rust projects. However, I am on a pretty deep level of yak-shaving now, so I am afraid that I’ll get a stackoverflow if try write to write this tool myself :slight_smile:

So, I’ll just through in the idea here, and hope that somebody picks it up and runs with it =)

When developing Rust code, I often have to run repetetive tasks. The example of such tasks are

  • generate some files to src/ (build.rs is not applicable here, b/c I want to commit the results of codegen)
  • add a new test data file with a specified format: for example, add files 00dd_foo.rs and 00dd_foo.txt to test/test_data/parsing/ok
  • check that each file with .rs extension starts with a license preamble
  • build and package a rust application
  • automatically update changelog file

Currently, I use just (a simpler and more sane make) for this, but I don’t quite like two things about this solution:

  • it relies on shell, and I am bad at shell scripting,
  • shells are bad at being cross-platform.

What I would really love is the ability to write this “scripts” in Rust…

Here is a short specification of the proposed solution

Specification

Conventions

By convention, all custom tasks are defined as a binary crate in the tasks subdirectory. For example, the layout for the frobnicator project might look like this:

frobnicator/
  Cargo.toml
  src/
    lib.rs
  tasks/
    Cargo.toml # this is **not** a member of workspace
    src/
      main.rs # this is the binary capable of executing all the tasks

The bare-bones way to run generate-test taks would look like this than:

$ cargo --manifest-path ./tasks/Cargo.toml run -- generate-test --name "foo"

Running Tasks

To run tasks in a convenient way, a special subcommand, cargo task is provided. So, the actual invocation looks like

$ cargo task generate-test --name "foo"

The job of cargo-task is:

  • find out the workspace root and cd in there
  • run cargo build --manifest-path ./tasks to build the tasks crate
  • run the resulting tasks binary, forwaring all the command-line arguments.

Bike shedding: should this be cargo do instead of cargo task? Or maybe even cargo x, to mirror ./x.py? Or maybe a fancy cargo swissknife?

Defining Tasks

In principle, it is possible to write arbitrary code in the tasks/src/main.rs. However a “shell-like scripting support library” would be most useful! Here are some tasks that this library might do:

  • reexport file crate API to make reading and wriing files easily
  • reexport other utilites to make it easy to read config files and env variables.
  • provide a way to conveniently launch subprocesses, with few lines of code and with automatic panicking on non-zero return code a-la set -e.
  • provide pure-rust implementations of common shell utilites as functions, like cd, ls, grep, find, sed.
  • provide automatic command rounting, with a strong convention over configuration API. That is, be simpler and less flexible than clap, the only way to define commands and options should be via serde.
  • provide a convenient way to shell out back to Cargo.

That’s about it for specification I guess?

As I’ve said, I don’t have enough time/desire to make this dream a reality, but, if someone else creates at least a bare bones version of it (basically, creates a repo with CI and implementation of cargo task), I promise I’ll use this thing for my project and will contribute stuff, necessary for me personally :slight_smile:


#2

I like this idea; and it’s pretty similar to mine, but I think a bit better.


#3

Could you expand on this a bit? Yours what? :slight_smile:


#4

Sorry, heh. I’ve been tossing around a design for this feature in my head for a few months now; I haven’t shared it with anyone yet because I didn’t like some aspects of it, which I think you’ve solved here :slight_smile:


#5

This is a little off topic, but what is the motivation for this? It seems to be a lot of duplication and maintenance (updating the year) when a top level license file should have the same effect.


#6

Sometimes folks from the legal department require copyright notice in each file. It certainly helps to detect copy-paste of portions of the software.

I don’t believe that one needs to annually bump a year though: it signifies the start of the copyright.

Fun fact: miniz has two different copyright notices, one at the start, and one at the end of the file: https://github.com/richgel999/miniz/blob/master/miniz.c


#7

I have seen cargo-make tool provides some cross platform scripting capabilities and compose tasks


#8

Like just, cargo-make focuses on defining tasks, while actually doing tasks is mostly left to the shell utilities.

I would prefer to define everything using just Rust. An eDSL for specifying tasks dependencies and such would be nice, but not necessary.


#9

Something like this seems pretty handy. What you are describing is essentially what Rake does for Ruby programmers.


#10

with cargo-make you can write tasks entirely in rust, look at:

[tasks.rust]
script_runner = "@rust"
script = [
'''
//! ```cargo
//! [dependencies]
//! time = "*"
//! ```
extern crate time;
fn main() {
    println!("{}", time::now().rfc822z());
}
'''
]

#11

Yeah, cargo-make looks really interesting! However, for my use-cases, I personally would prefer something less declarative, less powerful and not tied to Cargo.


#12

if less tied to cargo, I would recommand not calling it cargo-task :slight_smile:

I was also thinking about making cargo-make provide 2 executables, the first work via cargo and the other simple cli, same as rustfmt.
Not sure why haven’t done it by now :slight_smile:


#13

Turns out, Cargo already has all the features I need:

:slight_smile: