Thoughts: can we customize a 'run.rs' script just like what `build.rs` does now?

What I mentioned on the title means we may run a script for cargo run instead of executing the program itself. I believe this kind of feature, if there are, would somehow being convenience to Rust programmers.

To raise an example, recently I settled in on development on embedded Rust, and made a Rust library. That library is able to search via USB ports for flashers, enumerate all MCUs connected, and finally flash binary programs into them. This simple work is perfect to take place of OpenOCD other than one aspect: I have to wrap, to install my software, and finally type complex commands to do what I need.

As this library also written in Rust, if I could, I may write a program in a file called run.rs and configure it in Cargo.toml for any embedded rust applications. Inside that run.rs I could use the flasher written, and when I use cargo run command, binary files are produced before run.rs is executed, when the binary is flashed instead of executing directly in the operating system. I think this is what run implies - run on the target MCU chip. It would be far more convenient than having to install my software to do this.

One of my friends suggest another aspect that we may avoid using x.py file to build rustc itself by writing a run.rs script if we could. I don’t know if that’s possible, and if so, it could be awesome.

So is it necessary to customize a run.rs script, similar to the build.rs we have?


To who may concern, I expect a run.rs could contain something like this:

// include library for the run script
use nihao_stm32::{DevicesExt, FlashExt};
use futures::executor::block_on; 
// this function is to be executed on your computer, not your chip
fn main() -> std::io::Result<()> {
    // include the binary compiled from `src/main.rs` and relevant files
    // or we can do this through other run-time approaches
    let source = include!("./target/debug/my_program");
    // choose the MCU chip via custom library
    let mut chip = nihao::devices()?.filter_stm32f103()
        .next().expect("have a device plugged in").take()?;
    // perform flash and verify operations
    block_on!(nihao::Flash::new()
        .target(&mut chip)
        .erase_all()
        .program_entire(&source)
        .verify_crc(&source))?;
    // methods could block, but that's fine for a `run.rs` script
    // the source `src/main.rs` is now executed on the target chip
    block_on!(chip.send_continue())?;
    Ok(())
}   // when this execution is about to finish, it `drop`s all flashers
    // and chips in order to free handles and buffers behind them
// the actual running process terminates after `main` having executed.

Or if you want to try inter-chip connections like BLE or serial port, you may wish to flash into two chips without replugging any USB wires. In order to speed-up your development, the flashing methods can even be done asynchronously with non-blocking I/O functions and a proper thread pool or event system to manage them.

The runner field in .cargo/config can be used for this kind of thing: https://doc.rust-lang.org/cargo/reference/config.html#configuration-keys

4 Likes

Another way to achieve that is to make src/bin/runner.rs and use a plain old cargo run --bin runner

2 Likes

As you mentioned your crate is library I guess there isn’t src/main.rs in your code. Then why don’t you put your run script there?

1 Like

One advantage if this were a built-in feature is that it could automate the process of building the runner for the host, even while using --target something-else, just like we do for build scripts.

1 Like

As what I think, there do exists src/main.rs, and the compiled content of this file is to be flashed. If I had expressed correct, some script could be there to flash src/main.rs into the target chip, and I call it run.rs of something else. And actually the running script is being run on operating system installed in your PC, and the main.rs is to run on bare metal in the target chip.

I do not know if this is possible to put something like a special lint inside src/main.rs to describe the flashing script. That could be possible but may add tons of complexities as I consider.