diff --git a/objdiff-cli/src/cmd/diff.rs b/objdiff-cli/src/cmd/diff.rs index 96fb454f..7cf4edc8 100644 --- a/objdiff-cli/src/cmd/diff.rs +++ b/objdiff-cli/src/cmd/diff.rs @@ -81,12 +81,12 @@ pub struct Args { } pub fn run(args: Args) -> Result<()> { - let (target_path, base_path, project_config, unit_options, symbol_mappings) = + let (project_dir, target_path, base_path, project_config, unit_options, symbol_mappings) = match (&args.target, &args.base, &args.project, &args.unit) { (Some(_), Some(_), None, None) | (Some(_), None, None, None) | (None, Some(_), None, None) => { - (args.target.clone(), args.base.clone(), None, None, BTreeMap::new()) + (None, args.target.clone(), args.base.clone(), None, None, BTreeMap::new()) } (None, None, p, u) => { let project = match p { @@ -168,7 +168,14 @@ pub fn run(args: Args) -> Result<()> { let target_path = object.target_path.clone(); let base_path = object.base_path.clone(); let symbol_mappings = object.symbol_mappings.clone(); - (target_path, base_path, Some(project_config), unit_options, symbol_mappings) + ( + Some(project), + target_path, + base_path, + Some(project_config), + unit_options, + symbol_mappings, + ) } _ => bail!("Either target and base or project and unit must be specified"), }; @@ -184,7 +191,15 @@ pub fn run(args: Args) -> Result<()> { &symbol_mappings, ) } else { - run_interactive(args, target_path, base_path, project_config, unit_options, symbol_mappings) + run_interactive( + args, + project_dir, + target_path, + base_path, + project_config, + unit_options, + symbol_mappings, + ) } } @@ -345,6 +360,28 @@ impl AppState { fn check_jobs(&mut self) -> Result { let mut redraw = false; self.jobs.collect_results(); + // Surface job errors (e.g. a failed build) instead of silently showing nothing. + for job in self.jobs.iter_mut() { + let Some((title, error)) = job + .context + .status + .write() + .ok() + .and_then(|mut s| s.error.take().map(|e| (s.title.clone(), e))) + else { + continue; + }; + let status = BuildStatus { + success: false, + stdout: format!("Job \"{title}\" failed"), + stderr: format!("{error:#}"), + ..Default::default() + }; + self.left_status = Some(status.clone()); + self.right_status = Some(status); + redraw = true; + } + self.jobs.clear_finished(); for result in mem::take(&mut self.jobs.results) { match result { JobResult::None => unreachable!("Unexpected JobResult::None"), @@ -378,6 +415,7 @@ impl Wake for TermWaker { fn run_interactive( args: Args, + project_dir: Option, target_path: Option, base_path: Option, project_config: Option, @@ -396,7 +434,7 @@ fn run_interactive( let mut state = AppState { jobs: Default::default(), waker: Default::default(), - project_dir: args.project.clone(), + project_dir, project_config, target_path, base_path, diff --git a/objdiff-core/src/build/mod.rs b/objdiff-core/src/build/mod.rs index 248d653d..a88048df 100644 --- a/objdiff-core/src/build/mod.rs +++ b/objdiff-core/src/build/mod.rs @@ -4,6 +4,7 @@ use std::process::Command; use typed_path::{Utf8PlatformPathBuf, Utf8UnixPath}; +#[derive(Clone)] pub struct BuildStatus { pub success: bool, pub cmdline: String, diff --git a/objdiff-core/src/jobs/mod.rs b/objdiff-core/src/jobs/mod.rs index e3dddd5d..4b08db26 100644 --- a/objdiff-core/src/jobs/mod.rs +++ b/objdiff-core/src/jobs/mod.rs @@ -203,16 +203,22 @@ fn start_job( error: None, })); let context = JobContext { status: status.clone(), waker: waker.clone() }; - let context_inner = JobContext { status: status.clone(), waker }; + let context_inner = JobContext { status: status.clone(), waker: waker.clone() }; let (tx, rx) = std::sync::mpsc::channel(); - let handle = std::thread::spawn(move || match run(context_inner, rx) { - Ok(state) => state, - Err(e) => { - if let Ok(mut w) = status.write() { - w.error = Some(e); + let handle = std::thread::spawn(move || { + let result = match run(context_inner, rx) { + Ok(state) => state, + Err(e) => { + if let Ok(mut w) = status.write() { + w.error = Some(e); + } + JobResult::None } - JobResult::None - } + }; + // Always wake on completion, so the frontend notices jobs that finished + // (or failed) without reporting any progress. + waker.wake(); + result }); let id = JOB_ID.fetch_add(1, Ordering::Relaxed); JobState { id, kind, handle: Some(handle), context, cancel: tx }