-
Notifications
You must be signed in to change notification settings - Fork 214
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 file config support for ravedude #522
base: main
Are you sure you want to change the base?
Conversation
Also, some further notes:
# [...]
[avrdude]
programmer = "avrisp"
# partno = "t85"
baudrate = 19200
do_chip_erase = true Produces
|
nice! 👍
Feel free to drop the entire Whatever we end up doing with the "builtin boards", they should also simply get encoded in the new TOML format somewhere, as you suggested.
Interesting idea. My original design approach was to model Your suggestion reminds me of the approach of I don't see a big reason why not to go down this route, although I think we should consider the specifics carefully:
To put it into concrete examples: # Ravedude.toml for using well known board
[general]
console = true
baudrate = 57600
board = "uno" # Ravedude.toml for using a completely custom board
[general]
console = true
baudrate = 57600
[board]
name = "ATtiny85 through AVRISP"
[board.reset]
automatic = true
[board.avrdude]
programmer = "avrisp"
partno = "t85"
baudrate = 19200
do_chip_erase = true # Ravedude.toml for customizing well known board
[general]
console = true
baudrate = 57600
[board]
name = "Uno without automatic reset"
inherit = "uno"
[board.reset]
automatic = false What do you think? |
Sounds good!
I assume in this case command-line options would override the TOML file. Would we want a warning to be printed in that case?
Does this signify approval? Because I'd rather not change things just for the sake of changing things. If mandatory TOML configs don't benefit the project in any way, they shouldn't be implemented.
This is a very good point, and I hadn't considered that. Yeah, I agree that this would be a better solution. The examples look great! However, I would like a consistent case across keys. (Either snake_case or kebab-case is fine, although I'm leaning towards kebab case to mirror Cargo.toml.) Also, annoyingly, serde doesn't have "inheritance" behavior built-in, and from some very basic searching around the web I've gathered that the best way to do this is just to make everything an A final note/question: Would this be considered a breaking change...? If not, we'd need to find a way to keep this backwards-compatible. Maybe a "default" config that's used if the file doesn't exist? It depends on how much of a priority backwards compatibility is. |
Hi, sorry for only getting back to you now...
To me, it's really important not to completely break existing users. At least, there should be a trivial migration path that doesn't require deeper understanding of the inner workings of For simplicity, my suggestion would be to keep the existing invocations working as is, as long as no If all else fails, a hacky solution to do this might be to throw the current "legacy" argument parser into a separate module and invoke it when no
Hm, so let's look at all the CLI args we have right now and I'll share my thoughts for each one:
Generally, I wouldn't display warnings for the overrides. I'd only show warnings for deprecated things or errors when someone combines legacy invocations with a new
Yeah, let's do it. For the purpose that
Same here 👍 Thanks again for working on this! I'm excited to see the result :) This will make |
…ey are not, in fact, bugs
…ted error messages
Whew! Yeah, no worries, I've been busy as well... Anyways, a ton of stuff is complete! Additionally, it's now in a state where this could be merged, although a few (minor) things differ from your suggestions. Listing them off:
Additionally, using Ravedude.toml I have successfully uploaded a rust program to the ATtiny85 through the Arduino Nano! Anyways, yeah, unless we want to make changes to any of this (or if there are bugs), this PR is ready to merge! 🎉 (Not saying that it should be merged right now, but that it can be... there are also merge conflicts that need clearing up)
No problem! ravedude (and the entirety of avr-hal, really) makes my life a lot easier when using Rust for AVR-related projects! Happy to help :D |
Oh yeah -- We should also probably modify stuff like the avr-hal template to use Ravedude.toml, along with adding options for completely custom setups. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally found time to take a closer look. Thanks a lot for working on this, it's coming together quite nicely :)
Please check my comments below!
ravedude/src/main.rs
Outdated
warning!( | ||
"Passing the board as command-line argument is deprecated, use Ravedude.toml instead:\n\n# Ravedude.toml\n{}", | ||
toml::to_string(&config::RavedudeConfig::from_args(&args)?)? | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This warning is also shown when no commandline arguments are passed at all (and no Ravedude.toml
is present. This is a bit confusing, instead we should show an error about Ravedude.toml
being missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed 👍
ravedude/src/main.rs
Outdated
return Ok(()); | ||
} | ||
|
||
let ravedude_config_exists = std::path::Path::new("Ravedude.toml").try_exists()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now you are searching for Ravedude.toml
in the current working directory. This breaks when you e.g. cargo run
inside the src/
directory. Maybe you can check how other tools like cargo-embed
find their config file and implement something similar?
As a hint, it seems cargo sets some environment variables when running a runner process. One of them is CARGO_MANIFEST_DIR
which seems to indicate the directory where cargo found its own Cargo.toml
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cargo-embed
uses figment, which looks interesting. I'm most likely gonna switch ravedude over to it to mirror its behavior (and cargo's behavior) with environment variables, although I've held back on it for now due to it possibly being overkill (?) although ravedude itself isn't going to run on a microcontroller so that probably wasn't a good reason.
CARGO_MANIFEST_DIR
seems to be only available when using ravedude as a runner. ravedude should be able to access Ravedude.toml no matter how it's being run, in my opinion.
Currently, I just have ravedude scan all parent directories until it finds a Ravedude.toml
somewhere, which works fine for now... this behavior will probably be replaced by figment, though.
ravedude/src/config.rs
Outdated
pub open_console: Option<bool>, | ||
pub serial_baudrate: Option<NonZeroU32>, | ||
pub port: Option<std::path::PathBuf>, | ||
pub reset_delay: Option<u64>, | ||
pub board: Option<String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of making all fields Option<>
s, you can also leverage serde's default attribute. Where it makes sense, of course.
Here, I think open_console
would make much more sense that way:
#[serde(default)]
pub open_console: bool,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! The only other place where I think this would be sensible is do_chip_erase
in BoardAvrdudeOptions
, but I don't want to use #[serde(default)]
on it in case it's missing from the included boards.toml
and goes unnoticed.
#[serde(rename = "error")] | ||
Error(String), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we really let the config specify the error message here? I know the old ravedude had each board config manually error out, but we don't need to keep that without good reason.
I think it'd be better for the config to simply state that port guessing is not supported and ravedude then provides a standardized error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we really let the config specify the error message here?
Honestly, probably not. I just kept it in because that's how it was before and I didn't want to make modifications to the preexisting behavior.
I've removed it. Now the message is just
Warning: this board cannot reset itself.
Once reset, press ENTER here:
for all the boards without automatic reset. If the behavior should be changed, let me know!
ravedude/src/main.rs
Outdated
/// * duemilanove | ||
/// * duemilanovepriori |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this change about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, that's embarrassing. I must've accidentally pasted or typed something and somehow missed it. My bad, it's been removed.
// The board arg is taken before the binary, so rearrange the args when Ravedude.toml exists | ||
args.bin = Some(std::path::PathBuf::from(board)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parsing the binary path into a String
and then turning it back into a PathBuf
is not a good idea: PathBuf
is based on OsString
which can potentially represent things a String
can't. So doing this may error on certain paths because we try parsing them as utf-8. We should really handle the path opaquely as a PathBuf
all the way.
I think the easiest solution for the time being is to make board
a PathBuf
, too, and only attempt converting it to a String
in the legacy usecase. Or making board
an OsString
and then converting either direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍. board
is now an OsString
which gets converted into a String
when used without Ravedude.toml.
serialize_with = "serialize_baudrate", | ||
deserialize_with = "deserialize_baudrate" | ||
)] | ||
pub baudrate: Option<Option<NonZeroU32>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the double Option
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "inner" Option<NonZeroU32>
signifies the avrdude baudrate, or None
if it shouldn't be passed (i.e. use the default baudrate). The outer Option
is there to allow overriding the inherited board's baudrate:
[board]
inherit = "nano"
[board.avrdude]
# The nano has `baudrate = 57600` but it can be overriden.
# Some(None)
baudrate = -1
# Some(Some(NonZeroU32(8000))
baudrate = 8000
# None
# < no baudrate field set >
You can think of it as an Option<i32>
, but it's an Option<NonZeroU32>
instead of an i32
where -1
is mapped to None
. So, Option<Option<NonZeroU32>>
. If this is too convoluted we can change it back into an i32
.
use crate::config; | ||
|
||
fn get_all_boards() -> anyhow::Result<HashMap<String, config::BoardConfig>> { | ||
toml::from_str(include_str!("boards.toml")).map_err(|err| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me this include_str!()
is fine, I wouldn't waste time trying to parse the file at compile-time.
We should just make sure your testcase actually gets run in CI to catch errors in contributed pull-requests early. Can you adjust .github/workflows/ci.yml
to also run cargo test
for ravedude, in addition to cargo check
please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍
ravedude/src/board.rs
Outdated
struct ArduinoNanoNew; | ||
pub fn get_board(board_name: Option<&str>) -> anyhow::Result<config::RavedudeConfig> { | ||
Ok(match board_name { | ||
Some(board_name) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This match arm is just for the legacy usecase, right?
Wouldn't it make more sense to split this into separate functions? The newer code from None =>
also feels to me like it should be a part of config.rs
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍. Yes, the match arm was for passing a named board. Now there's two functions, get_board_from_name
and get_board_from_manifest
. They're still both in board.rs
because I feel like config.rs
should be relatively separate from how it's actually read from the filesystem (get_all_boards
which parses the included boards.toml
is also in board.rs
).
ravedude/src/board.rs
Outdated
assert!(board.name.is_some()); | ||
assert!(board.inherit.is_none()); | ||
assert!(board.reset.is_some()); | ||
assert!(board.avrdude.is_some()); | ||
let avrdude = board.avrdude.as_ref().unwrap(); | ||
assert!(avrdude.programmer.is_some()); | ||
assert!(avrdude.partno.is_some()); | ||
assert!(avrdude.baudrate.is_some()); | ||
assert!(avrdude.do_chip_erase.is_some()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bonus points for making these asserts show what board failed the test :) You can do it like this:
for (name, board) in all_boards.items() {
assert!(board.name.is_some(), "Board {name:?} is missing a `name`");
assert!(board.inherit.is_none(), "Board {name:?} has illegal `inherit` key");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍
…n warning showing up when it's not supposed to
… in board.rs, add test to ci
… not an Option<T>
Hi, again, sorry for the delay. I've made modifications to the code and left comments based on your feedback. Thanks! |
Followup PR to #521
What the title says. This adds a board named
custom
to ravedude that reads from Ravedude.toml from the project directory.Works with this config (in Ravedude.toml):
And this cargo runner:
Minor detail but this PR also changes
get_board
to returnanyhow::Result<Box<dyn Board>>
, if that matters.Anyways, format and implementation of custom board support in ravedude is up for discussion. If we reach a consensus about what would be best, this PR will be marked as ready for review.