Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
Canop committed Oct 8, 2024
2 parents b9ee1dc + 7b6491a commit b8cb636
Show file tree
Hide file tree
Showing 16 changed files with 286 additions and 56 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
### next
### next (will be 3.0)
#### Major feature: nextest support
Hit `n` to launch the nextest job.
It's a default job, but you may define your own one by specifying `analyzer = "nextest"` in the job entry.
Expand All @@ -9,11 +9,12 @@ If you're running a test or nextest job and you want only the failing test to be
If you want all tests to be executed again, hit `esc`.
Fix #214
#### Other features:
- grace period (by default 5ms) after a file event before the real launch of the command and during which other file events may be disregarded. Helps when saving a file changes several ones (eg backup then rename).
- new `exports` structure in configuration. New `analysis` export bound by default to `ctrl-e`. The old syntax defining locations export is still supported but won't appear in documentations anymore.
- recognize panic location in test - Fix #208
- lines to ignore can be specified as a set of regular expressions in a `ignored_lines` field either in the job or at the top of the prefs or bacon.toml - Fix #223
- `toggle-backtrace` accepts an optional level: `toggle-backtrace(1)` or `toggle-backtrace(full)` - Experimental - Fix #210
- configuration can be passed in `BACON_PREFS` and `BACON_CONFIG` env vars - Fix #76
- configuration paths can be passed in `BACON_PREFS` and `BACON_CONFIG` env vars - Fix #76
#### Fixes:
- fix changing wrapping mode not always working in raw output mode - Fix #234

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bacon"
version = "2.22.0-nextest"
version = "3.0.0-beta1"
authors = ["dystroy <denys.seguret@gmail.com>"]
repository = "https://github.com/Canop/bacon"
description = "background rust compiler"
Expand Down
10 changes: 9 additions & 1 deletion defaults/default-prefs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
#
# reverse = true

# The grace period is a delay after a file event before the real
# task is launched and during which other events will be ignored.
# This is useful if several events are often sent quasi-simultaneously
# (eg your editor backups before saving, then renames).
# You can set it to "none" if it's useless for you.
#
# grace_period = "5ms"

# Uncomment and change the value (true/false) to
# specify whether bacon should show a help line.
#
Expand Down Expand Up @@ -46,7 +54,7 @@
# (See https://dystroy.org/bacon/config/#export-locations),
#
# Possible line_format parts:
# - kind: warning|error|test
# - kind: warning|error|test
# - path: complete absolute path to the file
# - line: 1-based line number
# - column: 1-based column
Expand Down
15 changes: 10 additions & 5 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,22 @@ pub fn run(
Ok(we) => {
match we.kind {
EventKind::Modify(ModifyKind::Metadata(_)) => {
info!("ignoring metadata change");
debug!("ignoring metadata change");
return; // useless event
}
EventKind::Modify(ModifyKind::Data(DataChange::Any)) => {
info!("ignoring 'any' data change");
debug!("ignoring 'any' data change");
return; // probably useless event with no real change
}
EventKind::Access(AccessKind::Close(AccessMode::Write)) => {
info!("close write event: {we:?}");
debug!("close write event: {we:?}");
}
EventKind::Access(_) => {
info!("ignoring access event: {we:?}");
debug!("ignoring access event: {we:?}");
return; // probably useless event
}
_ => {
info!("notify event: {we:?}");
debug!("notify event: {we:?}");
}
}
if let Some(ignorer) = ignorer.as_mut() {
Expand Down Expand Up @@ -104,6 +104,11 @@ pub fn run(
let mut action: Option<&Action> = None;
select! {
recv(watch_receiver) -> _ => {
debug!("watch event received");
if task_executor.is_in_grace_period() {
debug!("ignoring notify event in grace period");
continue;
}
state.receive_watch_event();
if state.auto_refresh.is_enabled() {
if !state.is_computing() || on_change_strategy == OnChangeStrategy::KillThenRestart {
Expand Down
5 changes: 5 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ pub struct Config {
/// Patterns of lines which should be ignored. Patterns of
/// the prefs or bacon.toml can be overridden at the job
pub ignored_lines: Option<Vec<LinePattern>>,

/// The delay between a file event and the real start of the
/// task. Other file events occuring during this period will be
/// ignored.
pub grace_period: Option<Period>,
}

impl Config {
Expand Down
119 changes: 119 additions & 0 deletions src/exec/command_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::{
collections::HashMap,
ffi::{
OsStr,
OsString,
},
path::{
Path,
PathBuf,
},
process::{
Command,
Stdio,
},
};

#[derive(Debug, Clone)]
pub struct CommandBuilder {
exe: String,
current_dir: Option<PathBuf>,
args: Vec<OsString>,
with_stdout: bool,
envs: HashMap<OsString, OsString>,
}

impl CommandBuilder {
pub fn new(exe: &str) -> Self {
Self {
exe: exe.to_string(),
current_dir: None,
args: Vec::new(),
with_stdout: false,
envs: Default::default(),
}
}
pub fn build(&self) -> Command {
let mut command = Command::new(&self.exe);
if let Some(dir) = &self.current_dir {
command.current_dir(dir);
}
command.args(&self.args);
command.envs(&self.envs);
command
.envs(&self.envs)
.stdin(Stdio::null())
.stderr(Stdio::piped())
.stdout(if self.with_stdout {
Stdio::piped()
} else {
Stdio::null()
});
command
}
pub fn with_stdout(
&mut self,
b: bool,
) -> &mut Self {
self.with_stdout = b;
self
}
pub fn is_with_stdout(&self) -> bool {
self.with_stdout
}
pub fn current_dir<P: AsRef<Path>>(
&mut self,
dir: P,
) -> &mut Self {
self.current_dir = Some(dir.as_ref().to_path_buf());
self
}
pub fn arg<S: AsRef<OsStr>>(
&mut self,
arg: S,
) -> &mut Self {
self.args.push(arg.as_ref().to_os_string());
self
}
pub fn args<I, S>(
&mut self,
args: I,
) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
for arg in args {
self.args.push(arg.as_ref().to_os_string());
}
self
}
pub fn env<K, V>(
&mut self,
key: K,
val: V,
) -> &mut Self
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.envs
.insert(key.as_ref().to_os_string(), val.as_ref().to_os_string());
self
}
pub fn envs<I, K, V>(
&mut self,
vars: I,
) -> &mut Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
for (k, v) in vars {
self.envs
.insert(k.as_ref().to_os_string(), v.as_ref().to_os_string());
}
self
}
}
78 changes: 44 additions & 34 deletions src/executor.rs → src/exec/executor.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use {
crate::*,
anyhow::{
Context,
Result,
},
crossbeam::channel::{
Receiver,
Sender,
Expand All @@ -17,9 +13,9 @@ use {
process::{
Child,
Command,
Stdio,
},
thread,
time::Instant,
},
};

Expand All @@ -28,10 +24,8 @@ use {
/// and finishing by None.
/// Channel sizes are designed to avoid useless computations.
pub struct MissionExecutor {
command: Command,
command_builder: CommandBuilder,
kill_command: Option<Vec<String>>,
/// whether it's necessary to transmit stdout lines
with_stdout: bool,
line_sender: Sender<CommandExecInfo>,
pub line_receiver: Receiver<CommandExecInfo>,
}
Expand All @@ -42,6 +36,8 @@ pub struct TaskExecutor {
/// the thread running the current task
child_thread: thread::JoinHandle<()>,
stop_sender: Sender<StopMessage>,
grace_period_start: Option<Instant>, // forgotten at end of grace period
grace_period: Period,
}

/// A message sent to the child_thread on end
Expand All @@ -51,12 +47,6 @@ enum StopMessage {
Kill, // kill the process, don't bother about the status
}

/// Settings for one execution of job's command
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Task {
pub backtrace: Option<&'static str>,
}

impl TaskExecutor {
/// Interrupt the process
pub fn interrupt(self) {
Expand All @@ -71,27 +61,26 @@ impl TaskExecutor {
warn!("child_thread.join() failed"); // should not happen
}
}
pub fn is_in_grace_period(&mut self) -> bool {
if let Some(grace_period_start) = self.grace_period_start {
if grace_period_start.elapsed() < self.grace_period.duration {
return true;
}
self.grace_period_start = None;
}
false
}
}

impl MissionExecutor {
/// Prepare the executor (no task/process/thread is started at this point)
pub fn new(mission: &Mission) -> Result<Self> {
let mut command = mission.get_command();
pub fn new(mission: &Mission) -> anyhow::Result<Self> {
let command_builder = mission.get_command();
let kill_command = mission.kill_command();
let with_stdout = mission.need_stdout();
let (line_sender, line_receiver) = crossbeam::channel::unbounded();
command
.stdin(Stdio::null())
.stderr(Stdio::piped())
.stdout(if with_stdout {
Stdio::piped()
} else {
Stdio::null()
});
Ok(Self {
command,
command_builder,
kill_command,
with_stdout,
line_sender,
line_receiver,
})
Expand All @@ -101,21 +90,40 @@ impl MissionExecutor {
pub fn start(
&mut self,
task: Task,
) -> Result<TaskExecutor> {
) -> anyhow::Result<TaskExecutor> {
info!("start task {task:?}");
let mut child = self
.command
.env("RUST_BACKTRACE", task.backtrace.unwrap_or("0"))
.spawn()
.context("failed to launch command")?;
let grace_period = task.grace_period;
let grace_period_start = if grace_period.is_zero() {
None
} else {
Some(Instant::now())
};
let mut command_builder = self.command_builder.clone();
command_builder.env("RUST_BACKTRACE", task.backtrace.unwrap_or("0"));
let kill_command = self.kill_command.clone();
let with_stdout = self.with_stdout;
let with_stdout = command_builder.is_with_stdout();
let line_sender = self.line_sender.clone();
let (stop_sender, stop_receiver) = crossbeam::channel::bounded(1);
let err_stop_sender = stop_sender.clone();

// Global task executor thread
let child_thread = thread::spawn(move || {
// before starting the command, we wait some time, so that a bunch
// of quasi-simultaneous file events can be finished before the command
// starts (during this time, no other command is started by bacon in app.rs)
if !grace_period.is_zero() {
thread::sleep(grace_period.duration);
}

let child = command_builder.build().spawn();
let mut child = match child {
Ok(child) => child,
Err(e) => {
let _ = line_sender.send(CommandExecInfo::Error(e.to_string()));
return;
}
};

// thread piping the stdout lines
if with_stdout {
let sender = line_sender.clone();
Expand Down Expand Up @@ -208,6 +216,8 @@ impl MissionExecutor {
Ok(TaskExecutor {
child_thread,
stop_sender,
grace_period_start,
grace_period,
})
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/exec/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod command_builder;
mod executor;
mod period;
mod task;

pub use {
command_builder::CommandBuilder,
executor::*,
period::*,
task::Task,
};
Loading

0 comments on commit b8cb636

Please sign in to comment.