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

Add xtask crate #4293

Merged
merged 5 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
# we don't use `[build]` because of rust analyzer's build cache invalidation https://github.com/emilk/eframe_template/issues/93
[target.wasm32-unknown-unknown]
rustflags = ["--cfg=web_sys_unstable_apis"]

[alias]
xtask = "run --quiet --package xtask --"
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ members = [
"crates/epaint",

"examples/*",

"xtask",
]

[workspace.package]
Expand Down
20 changes: 1 addition & 19 deletions scripts/cargo_deny.sh
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
#!/usr/bin/env bash

set -eu
script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
cd "$script_path/.."
set -x

cargo install --quiet cargo-deny

cargo deny --all-features --log-level error --target aarch64-apple-darwin check
cargo deny --all-features --log-level error --target aarch64-linux-android check
cargo deny --all-features --log-level error --target i686-pc-windows-gnu check
cargo deny --all-features --log-level error --target i686-pc-windows-msvc check
cargo deny --all-features --log-level error --target i686-unknown-linux-gnu check
cargo deny --all-features --log-level error --target wasm32-unknown-unknown check
cargo deny --all-features --log-level error --target x86_64-apple-darwin check
cargo deny --all-features --log-level error --target x86_64-pc-windows-gnu check
cargo deny --all-features --log-level error --target x86_64-pc-windows-msvc check
cargo deny --all-features --log-level error --target x86_64-unknown-linux-gnu check
cargo deny --all-features --log-level error --target x86_64-unknown-linux-musl check
cargo deny --all-features --log-level error --target x86_64-unknown-redox check
cargo xtask deny
8 changes: 8 additions & 0 deletions xtask/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "xtask"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
version.workspace = true
emilk marked this conversation as resolved.
Show resolved Hide resolved

[dependencies]
12 changes: 12 additions & 0 deletions xtask/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## xtask - Task automation

This crate is meant to automate common tasks on the repository. It serves as a
replacement for shell scripts that is more portable across host operating
systems (namely Windows) and hopefully also easier to work with for
contributors who are already familiar with Rust (and not necessarily with shell
scripting).

The executable can be invoked via the subcommand `cargo xtask`, thanks to an
alias defined in `.cargo/config.toml`.

For more information, see <https://github.com/matklad/cargo-xtask>.
60 changes: 60 additions & 0 deletions xtask/src/deny.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! Run `cargo deny`
//!
//! Also installs the subcommand if it is not already installed.

use std::process::Command;

use super::DynError;

pub fn deny(args: &[&str]) -> Result<(), DynError> {
if !args.is_empty() {
return Err(format!("Invalid arguments: {args:?}").into());
}
install_cargo_deny()?;
let targets = [
"aarch64-apple-darwin",
"aarch64-linux-android",
"i686-pc-windows-gnu",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"wasm32-unknown-unknown",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnu",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-unknown-redox",
];
for target in targets {
let mut cmd = Command::new("cargo");
cmd.args([
"deny",
"--all-features",
"--log-level",
"error",
"--target",
target,
"check",
]);
super::utils::print_cmd(&cmd);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I just debug-print the command instead? That would print it like this:

"cargo" "deny" "--all-features" "--log-level" "error" "--target" "aarch64-apple-darwin" "check"

Which isn't how you would normally type it, but it works.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the pretty printing that you added!

let status = cmd.status()?;
if !status.success() {
return Err(status.to_string().into());
}
}
Ok(())
}

fn install_cargo_deny() -> Result<(), DynError> {
YgorSouza marked this conversation as resolved.
Show resolved Hide resolved
let already_installed = Command::new("cargo")
.args(["deny", "--version"])
.output()
.is_ok_and(|out| out.status.success());
if already_installed {
return Ok(());
}
let mut cmd = Command::new("cargo");
cmd.args(["+stable", "install", "--quiet", "--locked", "cargo-deny"]);
let reason = "install cargo-deny";
super::utils::ask_to_run(cmd, true, reason)
}
40 changes: 40 additions & 0 deletions xtask/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![allow(clippy::print_stdout)]
#![allow(clippy::print_stderr)]
#![allow(clippy::exit)]

mod deny;
pub(crate) mod utils;

type DynError = Box<dyn std::error::Error>;

fn main() {
if let Err(e) = try_main() {
eprintln!("{e}");
std::process::exit(-1);
}
}

fn try_main() -> Result<(), DynError> {
let arg_strings: Vec<_> = std::env::args().skip(1).collect();
let args: Vec<_> = arg_strings.iter().map(String::as_str).collect();

match args.as_slice() {
&[] | &["-h"] | &["--help"] => print_help(),
&["deny", ..] => deny::deny(&args[1..])?,
c => Err(format!("Invalid arguments {c:?}"))?,
}
Ok(())
}

fn print_help() {
let help = "
xtask help

Subcommands
deny: Run cargo-deny for all targets

Options
-h, --help: print help and exit
";
println!("{help}");
}
45 changes: 45 additions & 0 deletions xtask/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::{
env,
io::{self, Write as _},
process::Command,
};

use super::DynError;

/// Print the command and its arguments as if the user had typed them
pub fn print_cmd(cmd: &Command) {
print!("{} ", cmd.get_program().to_string_lossy());
for arg in cmd.get_args() {
print!("{} ", arg.to_string_lossy());
}
println!();
}

/// Prompt user before running a command
///
/// Adapted from [miri](https://github.com/rust-lang/miri/blob/dba35d2be72f4b78343d1a0f0b4737306f310672/cargo-miri/src/util.rs#L181-L204)
pub fn ask_to_run(mut cmd: Command, ask: bool, reason: &str) -> Result<(), DynError> {
// Disable interactive prompts in CI (GitHub Actions, Travis, AppVeyor, etc).
// Azure doesn't set `CI` though (nothing to see here, just Microsoft being Microsoft),
// so we also check their `TF_BUILD`.
let is_ci = env::var_os("CI").is_some() || env::var_os("TF_BUILD").is_some();
if ask && !is_ci {
let mut buf = String::new();
print!("The script is going to run: \n\n`{cmd:?}`\n\n To {reason}.\nProceed? [Y/n] ",);
io::stdout().flush().unwrap();
io::stdin().read_line(&mut buf).unwrap();
match buf.trim().to_lowercase().as_ref() {
"" | "y" | "yes" => {}
"n" | "no" => return Err("Aborting as per your request".into()),
a => return Err(format!("Invalid answer `{a}`").into()),
};
} else {
eprintln!("Running `{cmd:?}` to {reason}.");
}

let status = cmd.status()?;
if !status.success() {
return Err(format!("failed to {reason}: {status}").into());
}
Ok(())
}
Loading