2023-10-07 11:48:34 -07:00
|
|
|
use std::{
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
process::Command,
|
|
|
|
str::from_utf8,
|
|
|
|
sync::mpsc::Receiver,
|
|
|
|
};
|
2022-09-08 14:19:20 -07:00
|
|
|
|
2023-09-03 06:28:22 -07:00
|
|
|
use anyhow::{anyhow, Context, Error, Result};
|
2022-09-20 16:04:32 -07:00
|
|
|
use time::OffsetDateTime;
|
2022-09-08 14:19:20 -07:00
|
|
|
|
|
|
|
use crate::{
|
2023-10-07 11:48:34 -07:00
|
|
|
app::{AppConfig, ObjectConfig},
|
2023-11-21 08:48:18 -08:00
|
|
|
diff::{diff_objs, DiffAlg, DiffObjConfig},
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
2022-09-13 16:52:25 -07:00
|
|
|
obj::{elf, ObjInfo},
|
2022-09-08 14:19:20 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
pub struct BuildStatus {
|
|
|
|
pub success: bool,
|
|
|
|
pub log: String,
|
|
|
|
}
|
2022-12-06 14:53:32 -08:00
|
|
|
|
2023-10-07 11:48:34 -07:00
|
|
|
pub struct ObjDiffConfig {
|
|
|
|
pub build_base: bool,
|
|
|
|
pub build_target: bool,
|
|
|
|
pub custom_make: Option<String>,
|
|
|
|
pub project_dir: Option<PathBuf>,
|
|
|
|
pub selected_obj: Option<ObjectConfig>,
|
|
|
|
pub selected_wsl_distro: Option<String>,
|
2023-11-21 08:48:18 -08:00
|
|
|
pub code_alg: DiffAlg,
|
|
|
|
pub data_alg: DiffAlg,
|
2023-10-07 11:48:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ObjDiffConfig {
|
|
|
|
pub(crate) fn from_config(config: &AppConfig) -> Self {
|
|
|
|
ObjDiffConfig {
|
|
|
|
build_base: config.build_base,
|
|
|
|
build_target: config.build_target,
|
|
|
|
custom_make: config.custom_make.clone(),
|
|
|
|
project_dir: config.project_dir.clone(),
|
|
|
|
selected_obj: config.selected_obj.clone(),
|
|
|
|
selected_wsl_distro: config.selected_wsl_distro.clone(),
|
2023-11-21 08:48:18 -08:00
|
|
|
code_alg: config.code_alg,
|
|
|
|
data_alg: config.data_alg,
|
2023-10-07 11:48:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 16:52:25 -07:00
|
|
|
pub struct ObjDiffResult {
|
2022-09-08 14:19:20 -07:00
|
|
|
pub first_status: BuildStatus,
|
|
|
|
pub second_status: BuildStatus,
|
|
|
|
pub first_obj: Option<ObjInfo>,
|
|
|
|
pub second_obj: Option<ObjInfo>,
|
2022-09-20 16:04:32 -07:00
|
|
|
pub time: OffsetDateTime,
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
|
2023-10-07 11:48:34 -07:00
|
|
|
fn run_make(cwd: &Path, arg: &Path, config: &ObjDiffConfig) -> BuildStatus {
|
2022-09-08 14:19:20 -07:00
|
|
|
match (|| -> Result<BuildStatus> {
|
2022-12-06 14:53:32 -08:00
|
|
|
let make = config.custom_make.as_deref().unwrap_or("make");
|
2022-09-11 17:31:58 -07:00
|
|
|
#[cfg(not(windows))]
|
2022-09-11 17:45:27 -07:00
|
|
|
let mut command = {
|
2022-09-11 17:36:13 -07:00
|
|
|
let mut command = Command::new(make);
|
|
|
|
command.current_dir(cwd).arg(arg);
|
|
|
|
command
|
2022-09-11 17:45:27 -07:00
|
|
|
};
|
2022-09-11 17:31:58 -07:00
|
|
|
#[cfg(windows)]
|
|
|
|
let mut command = {
|
|
|
|
use std::os::windows::process::CommandExt;
|
2022-09-11 17:45:27 -07:00
|
|
|
|
|
|
|
use path_slash::PathExt;
|
2022-09-11 17:31:58 -07:00
|
|
|
let mut command = if config.selected_wsl_distro.is_some() {
|
|
|
|
Command::new("wsl")
|
|
|
|
} else {
|
|
|
|
Command::new(make)
|
|
|
|
};
|
|
|
|
if let Some(distro) = &config.selected_wsl_distro {
|
|
|
|
command
|
|
|
|
.arg("--cd")
|
|
|
|
.arg(cwd)
|
|
|
|
.arg("-d")
|
|
|
|
.arg(distro)
|
|
|
|
.arg("--")
|
|
|
|
.arg(make)
|
|
|
|
.arg(arg.to_slash_lossy().as_ref());
|
|
|
|
} else {
|
2022-09-11 18:08:18 -07:00
|
|
|
command.current_dir(cwd).arg(arg.to_slash_lossy().as_ref());
|
2022-09-11 17:31:58 -07:00
|
|
|
}
|
|
|
|
command.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
|
|
|
|
command
|
|
|
|
};
|
|
|
|
let output = command.output().context("Failed to execute build")?;
|
2022-09-08 14:19:20 -07:00
|
|
|
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
|
|
|
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
|
|
|
Ok(BuildStatus {
|
|
|
|
success: output.status.code().unwrap_or(-1) == 0,
|
2022-12-06 15:09:19 -08:00
|
|
|
log: format!("{stdout}\n{stderr}"),
|
2022-09-08 14:19:20 -07:00
|
|
|
})
|
|
|
|
})() {
|
|
|
|
Ok(status) => status,
|
|
|
|
Err(e) => BuildStatus { success: false, log: e.to_string() },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_build(
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
context: &JobContext,
|
2022-09-08 14:19:20 -07:00
|
|
|
cancel: Receiver<()>,
|
2023-10-07 11:48:34 -07:00
|
|
|
config: ObjDiffConfig,
|
2022-09-13 16:52:25 -07:00
|
|
|
) -> Result<Box<ObjDiffResult>> {
|
2023-09-03 06:28:22 -07:00
|
|
|
let obj_config = config.selected_obj.as_ref().ok_or_else(|| Error::msg("Missing obj path"))?;
|
2022-09-08 14:19:20 -07:00
|
|
|
let project_dir =
|
|
|
|
config.project_dir.as_ref().ok_or_else(|| Error::msg("Missing project dir"))?;
|
2023-09-09 20:43:12 -07:00
|
|
|
let target_path_rel = if let Some(target_path) = &obj_config.target_path {
|
|
|
|
Some(target_path.strip_prefix(project_dir).map_err(|_| {
|
|
|
|
anyhow!(
|
|
|
|
"Target path '{}' doesn't begin with '{}'",
|
|
|
|
target_path.display(),
|
|
|
|
project_dir.display()
|
|
|
|
)
|
2023-09-03 06:28:22 -07:00
|
|
|
})?)
|
2022-09-08 14:19:20 -07:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2023-09-09 20:43:12 -07:00
|
|
|
let base_path_rel = if let Some(base_path) = &obj_config.base_path {
|
|
|
|
Some(base_path.strip_prefix(project_dir).map_err(|_| {
|
|
|
|
anyhow!(
|
|
|
|
"Base path '{}' doesn't begin with '{}'",
|
|
|
|
base_path.display(),
|
|
|
|
project_dir.display()
|
|
|
|
)
|
2023-09-03 06:28:22 -07:00
|
|
|
})?)
|
2022-09-08 14:19:20 -07:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2023-09-09 20:43:12 -07:00
|
|
|
let mut total = 3;
|
|
|
|
if config.build_target && target_path_rel.is_some() {
|
|
|
|
total += 1;
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
2023-10-07 11:48:34 -07:00
|
|
|
if config.build_base && base_path_rel.is_some() {
|
2023-09-09 20:43:12 -07:00
|
|
|
total += 1;
|
|
|
|
}
|
|
|
|
let first_status = match target_path_rel {
|
|
|
|
Some(target_path_rel) if config.build_target => {
|
|
|
|
update_status(
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
context,
|
2023-09-09 20:43:12 -07:00
|
|
|
format!("Building target {}", target_path_rel.display()),
|
|
|
|
0,
|
|
|
|
total,
|
|
|
|
&cancel,
|
|
|
|
)?;
|
|
|
|
run_make(project_dir, target_path_rel, &config)
|
|
|
|
}
|
|
|
|
_ => BuildStatus { success: true, log: String::new() },
|
|
|
|
};
|
|
|
|
|
2023-10-07 11:48:34 -07:00
|
|
|
let second_status = match base_path_rel {
|
|
|
|
Some(base_path_rel) if config.build_base => {
|
|
|
|
update_status(
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
context,
|
2023-10-07 11:48:34 -07:00
|
|
|
format!("Building base {}", base_path_rel.display()),
|
|
|
|
0,
|
|
|
|
total,
|
|
|
|
&cancel,
|
|
|
|
)?;
|
|
|
|
run_make(project_dir, base_path_rel, &config)
|
|
|
|
}
|
|
|
|
_ => BuildStatus { success: true, log: String::new() },
|
2023-09-09 20:43:12 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let time = OffsetDateTime::now_utc();
|
|
|
|
|
|
|
|
let mut first_obj =
|
|
|
|
match &obj_config.target_path {
|
|
|
|
Some(target_path) if first_status.success => {
|
|
|
|
update_status(
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
context,
|
2023-09-09 20:43:12 -07:00
|
|
|
format!("Loading target {}", target_path_rel.unwrap().display()),
|
|
|
|
2,
|
|
|
|
total,
|
|
|
|
&cancel,
|
|
|
|
)?;
|
|
|
|
Some(elf::read(target_path).with_context(|| {
|
|
|
|
format!("Failed to read object '{}'", target_path.display())
|
|
|
|
})?)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut second_obj = match &obj_config.base_path {
|
|
|
|
Some(base_path) if second_status.success => {
|
|
|
|
update_status(
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
context,
|
2023-09-09 20:43:12 -07:00
|
|
|
format!("Loading base {}", base_path_rel.unwrap().display()),
|
|
|
|
3,
|
|
|
|
total,
|
|
|
|
&cancel,
|
|
|
|
)?;
|
|
|
|
Some(
|
|
|
|
elf::read(base_path)
|
|
|
|
.with_context(|| format!("Failed to read object '{}'", base_path.display()))?,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
update_status(context, "Performing diff".to_string(), 4, total, &cancel)?;
|
2023-11-21 08:48:18 -08:00
|
|
|
let diff_config = DiffObjConfig { code_alg: config.code_alg, data_alg: config.data_alg };
|
|
|
|
diff_objs(&diff_config, first_obj.as_mut(), second_obj.as_mut())?;
|
2022-09-08 14:19:20 -07:00
|
|
|
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
update_status(context, "Complete".to_string(), total, total, &cancel)?;
|
2022-09-20 16:04:32 -07:00
|
|
|
Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time }))
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
pub fn start_build(ctx: &egui::Context, config: ObjDiffConfig) -> JobState {
|
|
|
|
start_job(ctx, "Object diff", Job::ObjDiff, move |context, cancel| {
|
|
|
|
run_build(&context, cancel, config).map(|result| JobResult::ObjDiff(Some(result)))
|
2022-09-08 14:19:20 -07:00
|
|
|
})
|
|
|
|
}
|