-
-
Notifications
You must be signed in to change notification settings - Fork 71
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
Adds a watch flag to watch the elf file and reloads the file if it changed #807
base: main
Are you sure you want to change the base?
Conversation
Could this not be achieved with |
In theory yes, however afaik you'd usually run defmt-print by piping data from a serial port into it and I doubt |
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 generally looks OK to me, modulo a few comments.
I am a little bit worried about adding tokio to the defat-print dependency tree, but maybe someone can persuade me that this is better than running the TCP stream read in a thread with a 500ms timeout that checks some shared shutdown flag.
CHANGELOG.md
Outdated
@@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). | |||
- [#803]: `CI`: Disable nightly qemu-snapshot tests | |||
- [#789]: `defmt`: Add support for new time-related display hints | |||
- [#783]: `defmt-decoder`: Move formatting logic to `Formatter` | |||
- [#807]: `defmt-print`: Add watch_elf flrag to allow elf reload without restarting `defmt-print` |
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.
- [#807]: `defmt-print`: Add watch_elf flrag to allow elf reload without restarting `defmt-print` | |
- [#807]: `defmt-print`: Add `watch_elf` flag to allow ELF file reload without restarting `defmt-print` |
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.
⬆️
I tested this on macOS, and I the file watching didn't appear to work.
The problem here appears to be the notifier gave the full path, but I passed a relative path to defmt-print, and they don't match. So it never reloaded the file. |
print/src/main.rs
Outdated
@@ -40,11 +47,14 @@ struct Opts { | |||
#[arg(short = 'V', long)] | |||
version: bool, | |||
|
|||
#[arg[short, long]] |
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.
Let's be consistent
#[arg[short, long]] | |
#[arg(short, long)] |
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.
⬆️
Maybe it actually does make sense to use async. In the end @JomerDev Would you be willing to do that work? |
That would pretty much just mean that I'll split this PR apart, keeping the file watch stuff in here and move the async stuff into a separate PR, right? I can do that, sure |
Done, see #855 |
Please rebase on main. I will review next week. |
@JomerDev ping |
Sorry, I didn't have much time last week,. I'll try and get it done tomorrow |
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.
First round of review, still need to actually test it
print/src/main.rs
Outdated
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; | ||
|
||
use tokio::{ |
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.
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; | |
use tokio::{ | |
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; | |
use tokio::{ |
print/Cargo.toml
Outdated
log = "0.4" | ||
tokio = { version = "1.38", features = ["full"] } | ||
notify = "6.1.1" | ||
futures = "0.3" |
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.
Please keep alphabetic ordering
log = "0.4" | |
tokio = { version = "1.38", features = ["full"] } | |
notify = "6.1.1" | |
futures = "0.3" | |
futures = "0.3" | |
log = "0.4" | |
notify = "6.1" | |
tokio = { version = "1.38", features = ["full"] } |
print/src/main.rs
Outdated
let (tx, mut rx) = tokio::sync::mpsc::channel(1); | ||
|
||
let mut watcher = RecommendedWatcher::new( | ||
move |res| { | ||
futures::executor::block_on(async { | ||
tx.send(res).await.unwrap(); | ||
}) | ||
}, | ||
Config::default(), | ||
)?; | ||
let mut directory_path = opts.elf.clone().unwrap(); | ||
directory_path.pop(); // We want the elf directory instead of the elf, since some editors remove and recreate the file on save which will remove the notifier | ||
watcher.watch(directory_path.as_ref(), RecursiveMode::NonRecursive)?; | ||
|
||
let path = opts.elf.clone().unwrap(); | ||
|
||
loop { | ||
select! { | ||
r = run(opts.clone(), &mut source) => r?, | ||
_ = has_file_changed(&mut rx, &path) => () | ||
} | ||
} |
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.
Please move this to a run_and_watch
function
print/src/main.rs
Outdated
directory_path.pop(); // We want the elf directory instead of the elf, since some editors remove and recreate the file on save which will remove the notifier | ||
watcher.watch(directory_path.as_ref(), RecursiveMode::NonRecursive)?; |
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.
Could it be that we get notified about some unrelated changes? What if I am running two programs in parallel? Will changing one of the make both reload?
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.
We will, yes. But since there is no way to filter them directly in the watcher, we filter them in has_file_changed
instead. There we first check that the path matches the path of the elf file, and then we check the event kind and ignore everything that isn't a create or modify of that file
If you run two programs in parallel, as long as they not use the exact same elf file (with the exact same path) they wont both reload
decoder/Cargo.toml
Outdated
@@ -43,6 +43,7 @@ object = { version = "0.35", default-features = false, features = [ | |||
serde = { version = "1", features = ["derive"] } | |||
serde_json = { version = "1", features = ["arbitrary_precision"] } | |||
regex = "1" | |||
alterable_logger = "1.0.0" |
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.
alterable_logger = "1.0.0" | |
alterable_logger = "1" |
print/Cargo.toml
Outdated
@@ -20,3 +20,5 @@ defmt-decoder = { version = "=0.3.11", path = "../decoder", features = [ | |||
] } | |||
log = "0.4" | |||
tokio = { version = "1.38", features = ["full"] } | |||
notify = "6.1.1" |
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 disable crossbeam? See https://docs.rs/notify/latest/notify/index.html#crossbeam-channel--tokio
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.
Seeing as nobody could reliably confirm the issue and since std has been using the crossbeam implementation since 1.67 anyway I don't see a reason to do so.
See also the last comment on the issue: notify-rs/notify#380 (comment)
print/src/main.rs
Outdated
futures::executor::block_on(async { | ||
tx.send(res).await.unwrap(); | ||
}) |
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.
Would tokio::runtime::Handle.current() work here? Then we could get rid of the futures
dependency.
futures::executor::block_on(async { | |
tx.send(res).await.unwrap(); | |
}) | |
tokio::runtime::Handle::current().block_on(async { | |
tx.send(res).await.unwrap(); | |
}) |
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.
It's probably even easier to use tx.blocking_send
instead, that way there is no need for any async context. I'll try that later today
So, I don't know why the lint is complaining, I tried to apply it's suggestion but the rust-analyzer complains. I also don't get the lint when I run clippy locally |
This PR adds a --watch_elf/-w flag that watches the elf file for changes and reloads defmt-print on file modify or create.
To make this work I had to add tokio as dependency, since
stdin.read
otherwise blocked until some more serial data is received at which point it is too late to make the reload (if you do you end up with broken frames for the first few messages).I also had to add
alterable_logger
to defmt-decoder to allow for the global logger to be overwritten after it was already set.I'm open to making the flag and/or the logger changes depending on a feature.
This PR would fix issue #794
In verbose mode the logger also catches some logs from the notify crate used for the file watcher. This can probably be worked around if need be