├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── test1.sh ├── logo.png ├── test2.sh ├── justfile ├── CHANGELOG.md ├── Cargo.toml ├── src ├── cmd_ex.rs └── main.rs ├── tests └── simple.rs ├── LICENSE-MIT ├── Readme.md └── Cargo.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: rustunit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /test1.sh: -------------------------------------------------------------------------------- 1 | echo "wait 3" 2 | sleep 3 3 | echo "waited 3" -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustunit/parallelrun/HEAD/logo.png -------------------------------------------------------------------------------- /test2.sh: -------------------------------------------------------------------------------- 1 | RED='\033[0;31m' 2 | NC='\033[0m' # No Color 3 | printf "I ${RED}love${NC} Rust\n" 4 | echo "wait 2" 5 | sleep 2 6 | echo "waited 2" -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | default: 2 | just --list 3 | 4 | test-js: 5 | concurrently ./test1.sh ./test2.sh 6 | 7 | test-js-kill: 8 | concurrently --kill-others "./test1.sh" "./test2.sh" 9 | 10 | test: 11 | cargo r -- "./test1.sh" "./test2.sh" 12 | test-kill: 13 | cargo r -- -k "./test1.sh" "./test2.sh" 14 | 15 | ci: 16 | cargo fmt --all -- --check 17 | cargo c 18 | cargo t 19 | cargo clippy 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | ## [0.3.2] - 2024-10-04 11 | 12 | ### Fixes 13 | * Fix Windows build 14 | 15 | ## [0.3.1] - 2024-10-04 16 | 17 | ### Fixes 18 | * Do not register for SIGKILL prevent a crash 19 | 20 | ## [0.3.0] - 2024-10-04 21 | 22 | ### Added 23 | * Signals sent to parallelrun will be forwarded to child processes (supports SIGTERM, SIGINT, SIGHUP, SIGKILL, SIGQUIT) 24 | 25 | ## [0.2.0] - 2024-09-26 26 | 27 | ### Added 28 | * Add windows support and ci -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parallelrun" 3 | version = "0.3.2" 4 | edition = "2021" 5 | authors = ["extrawurst "] 6 | description = "Runs several commands concurrently" 7 | homepage = "https://github.com/rustunit/parallelrun" 8 | repository = "https://github.com/rustunit/parallelrun" 9 | readme = "Readme.md" 10 | license = "MIT" 11 | categories = ["command-line-utilities"] 12 | keywords = ["cli", "terminal", "ui"] 13 | 14 | [dependencies] 15 | crossbeam = "0.8" 16 | clap = { version = "4.5", features = ["derive"] } 17 | sysinfo = "0.31" 18 | signal-hook = "0.3.17" 19 | anyhow = "1.0.89" 20 | 21 | [workspace.lints.rust] 22 | warnings = "deny" 23 | unused_imports = "deny" 24 | unused_variables = "deny" 25 | unused_mut = "deny" 26 | dead_code = "warn" 27 | unsafe_code = "deny" 28 | 29 | [workspace.lints.clippy] 30 | all = "deny" 31 | -------------------------------------------------------------------------------- /src/cmd_ex.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | pub trait CommandExt { 4 | fn with_no_window(&mut self) -> &mut Self; 5 | } 6 | 7 | impl CommandExt for Command { 8 | /// On Windows, CLI applications that aren't the window's subsystem will 9 | /// create and show a console window that pops up next to the main 10 | /// application window when run. We disable this behavior by setting the 11 | /// `CREATE_NO_WINDOW` flag. 12 | /// see https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags 13 | #[inline] 14 | fn with_no_window(&mut self) -> &mut Self { 15 | #[cfg(windows)] 16 | { 17 | use std::os::windows::process::CommandExt; 18 | self.creation_flags(0x0800_0000) 19 | } 20 | 21 | #[cfg(not(windows))] 22 | { 23 | self 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/simple.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | #[test] 4 | fn test_simple() { 5 | let output = Command::new("target/debug/parallelrun") 6 | .args(vec!["echo 1 && sleep 1", "sleep 2 && echo 2"]) 7 | .output() 8 | .unwrap(); 9 | 10 | let expected_output = r"[0] 1 11 | [0] echo 1 && sleep 1 exited with code 0 12 | [1] 2 13 | [1] sleep 2 && echo 2 exited with code 0 14 | "; 15 | assert_eq!(&String::from_utf8_lossy(&output.stdout), expected_output); 16 | } 17 | 18 | #[test] 19 | fn test_kill() { 20 | let output = Command::new("target/debug/parallelrun") 21 | .args(vec!["echo 1 && sleep 2", "sleep 1 && echo 2 && sleep 2"]) 22 | .output() 23 | .unwrap(); 24 | 25 | let expected_output = r"[0] 1 26 | [1] 2 27 | [0] echo 1 && sleep 2 exited with code 0 28 | [1] sleep 1 && echo 2 && sleep 2 exited with code 0 29 | "; 30 | assert_eq!(&String::from_utf8_lossy(&output.stdout), expected_output); 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [ "master" ] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | # Cancels in progress workflows on PRs. 11 | cancel-in-progress: ${{ github.event_name == 'pull_request' }} 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | fail-fast: true 20 | matrix: 21 | os: [ubuntu-latest, macos-latest, windows-latest] 22 | runs-on: ${{ matrix.os }} 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Restore cargo cache 28 | uses: Swatinem/rust-cache@v2 29 | 30 | - name: Install Rust 31 | uses: dtolnay/rust-toolchain@stable 32 | with: 33 | components: clippy 34 | 35 | - name: Rustup Show 36 | run: rustup show 37 | 38 | - name: Cargo fmt --check 39 | run: cargo fmt --all -- --check 40 | 41 | - name: Cargo Check 42 | run: cargo c 43 | 44 | - name: Cargo Test 45 | run: cargo t 46 | 47 | - name: Cargo Clippy 48 | run: cargo clippy -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # parallelrun 2 | 3 | [![crates.io][sh_crates]][lk_crates] 4 | [![ci][sh_ci]][lk_ci] 5 | [![discord][sh_discord]][lk_discord] 6 | 7 | [sh_crates]: https://img.shields.io/crates/v/parallelrun.svg 8 | [lk_crates]: https://crates.io/crates/parallelrun 9 | [sh_ci]: https://github.com/rustunit/parallelrun/workflows/ci/badge.svg 10 | [lk_ci]: https://github.com/rustunit/parallelrun/actions 11 | [sh_discord]: https://img.shields.io/discord/1176858176897953872?label=discord&color=5561E6 12 | [lk_discord]: https://discord.gg/rQNeEnMhus 13 | 14 | ![logo](./logo.png) 15 | 16 | Runs several commands concurrently. 17 | 18 | Heavily inspired by the `nodejs` tool [concurrently](https://www.npmjs.com/package/concurrently). 19 | 20 | Supported and tested on Linux, MacOS and Windows. 21 | 22 | Supported Options: 23 | * `--kill-others` (terminates all other commands as soon as one exits) 24 | 25 | # Demo 26 | 27 | [![demo](https://asciinema.org/a/677736.svg)](https://asciinema.org/a/677736?autoplay=1) 28 | 29 | # Installation 30 | 31 | ``` 32 | cargo install parallelrun 33 | ``` 34 | 35 | # Example 36 | 37 | ```sh 38 | $ parallelrun --kill-others "echo wait 2 && sleep 2" "echo wait 3 && sleep 3" 39 | [0] wait 2 40 | [1] wait 3 41 | [0] echo wait 2 && sleep 2 exited with code 0 42 | --> Sending SIGTERM to other processes.. 43 | [1] echo wait 3 && sleep 3 exited with code SIGTERM 44 | ``` 45 | 46 | # TODO 47 | 48 | - [ ] support more `concurrently` arguments -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod cmd_ex; 2 | 3 | use std::{ 4 | io::{BufRead, BufReader, Write}, 5 | process::{Child, Command, Stdio}, 6 | sync::{ 7 | atomic::{AtomicBool, Ordering}, 8 | Arc, 9 | }, 10 | thread::{self, JoinHandle}, 11 | }; 12 | 13 | use clap::Parser; 14 | use cmd_ex::CommandExt; 15 | use crossbeam::channel::{bounded, Receiver}; 16 | use sysinfo::Signal; 17 | 18 | #[derive(Default, Parser, Debug)] 19 | #[command(version)] 20 | struct Arguments { 21 | #[arg( 22 | default_value_t = false, 23 | short, 24 | long, 25 | help = "Kill other processes if one exits or dies. [bool]" 26 | )] 27 | kill_others: bool, 28 | 29 | #[arg( 30 | value_name = "COMMANDS", 31 | help = "List of commands to run. String escaped and space separated. ['cmd1' 'cmd2'...]" 32 | )] 33 | cmd: Vec, 34 | } 35 | 36 | fn main() -> anyhow::Result<()> { 37 | let args = Arguments::parse(); 38 | 39 | spawn_cmds(&args.cmd, args.kill_others, register_signals()?) 40 | } 41 | 42 | fn register_signals() -> anyhow::Result>> { 43 | #[cfg(unix)] 44 | { 45 | use signal_hook::{consts::*, iterator::Signals}; 46 | 47 | let (tx, rx) = bounded::(1); 48 | let mut signals = Signals::new([SIGTERM, SIGINT, SIGHUP, SIGQUIT])?; 49 | 50 | thread::spawn(move || { 51 | let tx = tx.clone(); 52 | for sig in signals.forever() { 53 | if sig != SIGINT { 54 | tx.send(match sig { 55 | SIGINT => Signal::Interrupt, 56 | SIGHUP => Signal::Hangup, 57 | SIGKILL => Signal::Kill, 58 | SIGQUIT => Signal::Quit, 59 | _ => Signal::Term, 60 | }) 61 | .expect("problem sending signal"); 62 | } 63 | } 64 | }); 65 | 66 | Ok(Some(rx)) 67 | } 68 | 69 | #[cfg(not(unix))] 70 | Ok(None) 71 | } 72 | 73 | fn spawn_cmds( 74 | args: &[String], 75 | kill_others: bool, 76 | signal_receiver: Option>, 77 | ) -> anyhow::Result<()> { 78 | let processes = args 79 | .iter() 80 | .enumerate() 81 | .map(|(i, arg)| (i, arg.to_string(), spawn_child(i, arg))) 82 | .collect::>(); 83 | 84 | let mut handles: Vec<(u32, JoinHandle<()>)> = vec![]; 85 | let (tx, rx) = bounded::(1); 86 | 87 | for (i, cmd, mut process) in processes { 88 | let tx = tx.clone(); 89 | handles.push(( 90 | process.id(), 91 | std::thread::spawn(move || { 92 | let result = process.wait(); 93 | 94 | let code = match result { 95 | Ok(status) => status_to_string(status), 96 | Err(_) => String::from("?"), 97 | }; 98 | 99 | println!("[{i}] {cmd} exited with code {code}"); 100 | 101 | if kill_others { 102 | let _ = tx.send(Signal::Term); 103 | } 104 | }), 105 | )); 106 | } 107 | 108 | let process_ids = handles.iter().map(|(id, _)| *id).collect::>(); 109 | 110 | let signal_in_flight = Arc::new(AtomicBool::new(false)); 111 | 112 | if let Some(signal_receiver) = signal_receiver { 113 | thread::spawn({ 114 | let pids = process_ids.clone(); 115 | let signal_in_flight = signal_in_flight.clone(); 116 | move || { 117 | if let Ok(signal) = signal_receiver.recv() { 118 | signal_in_flight.store(true, Ordering::Relaxed); 119 | process_killer(pids, signal); 120 | } 121 | } 122 | }); 123 | } 124 | 125 | if kill_others { 126 | thread::spawn({ 127 | let signal_in_flight = signal_in_flight.clone(); 128 | move || { 129 | if let Ok(signal) = rx.recv() { 130 | if !signal_in_flight.load(Ordering::Relaxed) { 131 | process_killer(process_ids, signal); 132 | } 133 | } 134 | } 135 | }); 136 | } 137 | 138 | for (_, handle) in handles { 139 | let _ = handle.join(); 140 | } 141 | 142 | Ok(()) 143 | } 144 | 145 | #[cfg(unix)] 146 | fn status_to_string(status: std::process::ExitStatus) -> String { 147 | use std::os::unix::process::ExitStatusExt; 148 | 149 | if let Some(signal) = status.signal() { 150 | signal_as_string(signal).into() 151 | } else if let Some(code) = status.code() { 152 | format!("{code}") 153 | } else { 154 | unreachable!() 155 | } 156 | } 157 | 158 | #[cfg(unix)] 159 | #[allow(non_snake_case)] 160 | fn signal_as_string(sig: i32) -> &'static str { 161 | use signal_hook::consts::*; 162 | 163 | //TODO: is there a library for SIG to String? 164 | match sig { 165 | SIGHUP => "SIGHUP", 166 | SIGINT => "SIGINT", 167 | SIGQUIT => "SIGQUIT", 168 | SIGILL => "SIGILL", 169 | SIGABRT => "SIGABRT", 170 | SIGFPE => "SIGFPE", 171 | SIGKILL => "SIGKILL", 172 | SIGSEGV => "SIGSEGV", 173 | SIGPIPE => "SIGPIPE", 174 | SIGALRM => "SIGALRM", 175 | SIGTERM => "SIGTERM", 176 | _ => "Unknown Signal", 177 | } 178 | } 179 | 180 | #[cfg(not(unix))] 181 | fn signal_as_string(sig: i32) -> &'static str { 182 | "unkonwn signal" 183 | } 184 | 185 | #[cfg(not(unix))] 186 | fn status_to_string(status: std::process::ExitStatus) -> String { 187 | if let Some(code) = status.code() { 188 | format!("{code}") 189 | } else { 190 | unreachable!() 191 | } 192 | } 193 | 194 | fn process_killer(process_ids: Vec, sig: Signal) { 195 | println!("--> Sending {sig} Signal to other processes.."); 196 | for pid in process_ids { 197 | sig_term(pid, sig); 198 | } 199 | } 200 | 201 | fn sig_term(pid: u32, sig: Signal) { 202 | use sysinfo::{Pid, System, SUPPORTED_SIGNALS}; 203 | let pid = Pid::from_u32(pid); 204 | let s = System::new_all(); 205 | 206 | if let Some(process) = s.process(pid) { 207 | if SUPPORTED_SIGNALS.contains(&sig) { 208 | process.kill_with(sig).expect("kill_with failed"); 209 | } else { 210 | process.kill(); 211 | } 212 | } 213 | } 214 | 215 | fn spawn_child(idx: usize, cmd: &str) -> Child { 216 | let mut child = Command::new("sh") 217 | .arg("-c") 218 | .arg(cmd) 219 | .with_no_window() 220 | .stdout(Stdio::piped()) 221 | .spawn() 222 | .expect("failed to spawn child process"); 223 | 224 | let child_out = child.stdout.take().expect("Failed to open stdout"); 225 | std::thread::spawn(move || { 226 | let stdout_reader = BufReader::new(child_out); 227 | let stdout_lines = stdout_reader.lines(); 228 | 229 | let mut stdout = std::io::stdout(); 230 | 231 | for line in stdout_lines.map_while(Result::ok) { 232 | print!("[{idx}] "); 233 | stdout 234 | .write_all(line.as_bytes()) 235 | .expect("Failed to write to stdout"); 236 | println!(); 237 | } 238 | }); 239 | 240 | child 241 | } 242 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.8" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 40 | dependencies = [ 41 | "windows-sys", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.4" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 49 | dependencies = [ 50 | "anstyle", 51 | "windows-sys", 52 | ] 53 | 54 | [[package]] 55 | name = "anyhow" 56 | version = "1.0.89" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 59 | 60 | [[package]] 61 | name = "clap" 62 | version = "4.5.17" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" 65 | dependencies = [ 66 | "clap_builder", 67 | "clap_derive", 68 | ] 69 | 70 | [[package]] 71 | name = "clap_builder" 72 | version = "4.5.17" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" 75 | dependencies = [ 76 | "anstream", 77 | "anstyle", 78 | "clap_lex", 79 | "strsim", 80 | ] 81 | 82 | [[package]] 83 | name = "clap_derive" 84 | version = "4.5.13" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 87 | dependencies = [ 88 | "heck", 89 | "proc-macro2", 90 | "quote", 91 | "syn", 92 | ] 93 | 94 | [[package]] 95 | name = "clap_lex" 96 | version = "0.7.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 99 | 100 | [[package]] 101 | name = "colorchoice" 102 | version = "1.0.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 105 | 106 | [[package]] 107 | name = "core-foundation-sys" 108 | version = "0.8.7" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 111 | 112 | [[package]] 113 | name = "crossbeam" 114 | version = "0.8.4" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" 117 | dependencies = [ 118 | "crossbeam-channel", 119 | "crossbeam-deque", 120 | "crossbeam-epoch", 121 | "crossbeam-queue", 122 | "crossbeam-utils", 123 | ] 124 | 125 | [[package]] 126 | name = "crossbeam-channel" 127 | version = "0.5.13" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" 130 | dependencies = [ 131 | "crossbeam-utils", 132 | ] 133 | 134 | [[package]] 135 | name = "crossbeam-deque" 136 | version = "0.8.5" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 139 | dependencies = [ 140 | "crossbeam-epoch", 141 | "crossbeam-utils", 142 | ] 143 | 144 | [[package]] 145 | name = "crossbeam-epoch" 146 | version = "0.9.18" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 149 | dependencies = [ 150 | "crossbeam-utils", 151 | ] 152 | 153 | [[package]] 154 | name = "crossbeam-queue" 155 | version = "0.3.11" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" 158 | dependencies = [ 159 | "crossbeam-utils", 160 | ] 161 | 162 | [[package]] 163 | name = "crossbeam-utils" 164 | version = "0.8.20" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 167 | 168 | [[package]] 169 | name = "either" 170 | version = "1.13.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 173 | 174 | [[package]] 175 | name = "heck" 176 | version = "0.5.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 179 | 180 | [[package]] 181 | name = "is_terminal_polyfill" 182 | version = "1.70.1" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 185 | 186 | [[package]] 187 | name = "libc" 188 | version = "0.2.158" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 191 | 192 | [[package]] 193 | name = "memchr" 194 | version = "2.7.4" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 197 | 198 | [[package]] 199 | name = "ntapi" 200 | version = "0.4.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 203 | dependencies = [ 204 | "winapi", 205 | ] 206 | 207 | [[package]] 208 | name = "parallelrun" 209 | version = "0.3.2" 210 | dependencies = [ 211 | "anyhow", 212 | "clap", 213 | "crossbeam", 214 | "signal-hook", 215 | "sysinfo", 216 | ] 217 | 218 | [[package]] 219 | name = "proc-macro2" 220 | version = "1.0.86" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 223 | dependencies = [ 224 | "unicode-ident", 225 | ] 226 | 227 | [[package]] 228 | name = "quote" 229 | version = "1.0.37" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 232 | dependencies = [ 233 | "proc-macro2", 234 | ] 235 | 236 | [[package]] 237 | name = "rayon" 238 | version = "1.10.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 241 | dependencies = [ 242 | "either", 243 | "rayon-core", 244 | ] 245 | 246 | [[package]] 247 | name = "rayon-core" 248 | version = "1.12.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 251 | dependencies = [ 252 | "crossbeam-deque", 253 | "crossbeam-utils", 254 | ] 255 | 256 | [[package]] 257 | name = "signal-hook" 258 | version = "0.3.17" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 261 | dependencies = [ 262 | "libc", 263 | "signal-hook-registry", 264 | ] 265 | 266 | [[package]] 267 | name = "signal-hook-registry" 268 | version = "1.4.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 271 | dependencies = [ 272 | "libc", 273 | ] 274 | 275 | [[package]] 276 | name = "strsim" 277 | version = "0.11.1" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 280 | 281 | [[package]] 282 | name = "syn" 283 | version = "2.0.77" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" 286 | dependencies = [ 287 | "proc-macro2", 288 | "quote", 289 | "unicode-ident", 290 | ] 291 | 292 | [[package]] 293 | name = "sysinfo" 294 | version = "0.31.4" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" 297 | dependencies = [ 298 | "core-foundation-sys", 299 | "libc", 300 | "memchr", 301 | "ntapi", 302 | "rayon", 303 | "windows", 304 | ] 305 | 306 | [[package]] 307 | name = "unicode-ident" 308 | version = "1.0.13" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 311 | 312 | [[package]] 313 | name = "utf8parse" 314 | version = "0.2.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 317 | 318 | [[package]] 319 | name = "winapi" 320 | version = "0.3.9" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 323 | dependencies = [ 324 | "winapi-i686-pc-windows-gnu", 325 | "winapi-x86_64-pc-windows-gnu", 326 | ] 327 | 328 | [[package]] 329 | name = "winapi-i686-pc-windows-gnu" 330 | version = "0.4.0" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 333 | 334 | [[package]] 335 | name = "winapi-x86_64-pc-windows-gnu" 336 | version = "0.4.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 339 | 340 | [[package]] 341 | name = "windows" 342 | version = "0.57.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" 345 | dependencies = [ 346 | "windows-core", 347 | "windows-targets", 348 | ] 349 | 350 | [[package]] 351 | name = "windows-core" 352 | version = "0.57.0" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" 355 | dependencies = [ 356 | "windows-implement", 357 | "windows-interface", 358 | "windows-result", 359 | "windows-targets", 360 | ] 361 | 362 | [[package]] 363 | name = "windows-implement" 364 | version = "0.57.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" 367 | dependencies = [ 368 | "proc-macro2", 369 | "quote", 370 | "syn", 371 | ] 372 | 373 | [[package]] 374 | name = "windows-interface" 375 | version = "0.57.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" 378 | dependencies = [ 379 | "proc-macro2", 380 | "quote", 381 | "syn", 382 | ] 383 | 384 | [[package]] 385 | name = "windows-result" 386 | version = "0.1.2" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" 389 | dependencies = [ 390 | "windows-targets", 391 | ] 392 | 393 | [[package]] 394 | name = "windows-sys" 395 | version = "0.52.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 398 | dependencies = [ 399 | "windows-targets", 400 | ] 401 | 402 | [[package]] 403 | name = "windows-targets" 404 | version = "0.52.6" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 407 | dependencies = [ 408 | "windows_aarch64_gnullvm", 409 | "windows_aarch64_msvc", 410 | "windows_i686_gnu", 411 | "windows_i686_gnullvm", 412 | "windows_i686_msvc", 413 | "windows_x86_64_gnu", 414 | "windows_x86_64_gnullvm", 415 | "windows_x86_64_msvc", 416 | ] 417 | 418 | [[package]] 419 | name = "windows_aarch64_gnullvm" 420 | version = "0.52.6" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 423 | 424 | [[package]] 425 | name = "windows_aarch64_msvc" 426 | version = "0.52.6" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 429 | 430 | [[package]] 431 | name = "windows_i686_gnu" 432 | version = "0.52.6" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 435 | 436 | [[package]] 437 | name = "windows_i686_gnullvm" 438 | version = "0.52.6" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 441 | 442 | [[package]] 443 | name = "windows_i686_msvc" 444 | version = "0.52.6" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 447 | 448 | [[package]] 449 | name = "windows_x86_64_gnu" 450 | version = "0.52.6" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 453 | 454 | [[package]] 455 | name = "windows_x86_64_gnullvm" 456 | version = "0.52.6" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 459 | 460 | [[package]] 461 | name = "windows_x86_64_msvc" 462 | version = "0.52.6" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 465 | --------------------------------------------------------------------------------