mirror of https://github.com/encounter/objdiff.git
Improved Windows support & simple WSL2 integration
This commit is contained in:
parent
b55c919f4d
commit
daaa5c86a2
|
@ -1576,6 +1576,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"notify",
|
"notify",
|
||||||
"object",
|
"object",
|
||||||
|
"path-slash",
|
||||||
"ppc750cl",
|
"ppc750cl",
|
||||||
"rabbitizer",
|
"rabbitizer",
|
||||||
"rfd",
|
"rfd",
|
||||||
|
@ -1583,6 +1584,7 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-wasm",
|
"tracing-wasm",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1660,6 +1662,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "path-slash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|
|
@ -27,6 +27,10 @@ egui_extras = "0.19.0"
|
||||||
ppc750cl = { git = "https://github.com/terorie/ppc750cl" }
|
ppc750cl = { git = "https://github.com/terorie/ppc750cl" }
|
||||||
rabbitizer = { git = "https://github.com/encounter/rabbitizer-rs", rev = "10c279b2ef251c62885b1dcdcfe740b0db8e9956" }
|
rabbitizer = { git = "https://github.com/encounter/rabbitizer-rs", rev = "10c279b2ef251c62885b1dcdcfe740b0db8e9956" }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
path-slash = "0.2.0"
|
||||||
|
winapi = "0.3.9"
|
||||||
|
|
||||||
# native:
|
# native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
|
10
src/app.rs
10
src/app.rs
|
@ -61,6 +61,11 @@ pub struct ViewState {
|
||||||
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
|
pub custom_make: String,
|
||||||
|
// WSL2 settings
|
||||||
|
#[serde(skip)]
|
||||||
|
pub available_wsl_distros: Option<Vec<String>>,
|
||||||
|
pub selected_wsl_distro: Option<String>,
|
||||||
// Split obj
|
// Split obj
|
||||||
pub project_dir: Option<PathBuf>,
|
pub project_dir: Option<PathBuf>,
|
||||||
pub build_asm_dir: Option<PathBuf>,
|
pub build_asm_dir: Option<PathBuf>,
|
||||||
|
@ -156,7 +161,6 @@ impl eframe::App for App {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
||||||
ui.heading("Config");
|
|
||||||
config_ui(ui, config, view_state);
|
config_ui(ui, config, view_state);
|
||||||
jobs_ui(ui, view_state);
|
jobs_ui(ui, view_state);
|
||||||
});
|
});
|
||||||
|
@ -194,7 +198,9 @@ impl eframe::App for App {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
});
|
});
|
||||||
|
|
||||||
if view_state.jobs.iter().any(|job| {
|
// Windows + request_repaint_after breaks dialogs:
|
||||||
|
// https://github.com/emilk/egui/issues/2003
|
||||||
|
if cfg!(windows) || view_state.jobs.iter().any(|job| {
|
||||||
if let Some(handle) = &job.handle {
|
if let Some(handle) = &job.handle {
|
||||||
return !handle.is_finished();
|
return !handle.is_finished();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{
|
||||||
path::Path,
|
path::Path,
|
||||||
process::Command,
|
process::Command,
|
||||||
str::from_utf8,
|
str::from_utf8,
|
||||||
sync::{mpsc::Receiver, Arc, RwLock},
|
sync::{Arc, mpsc::Receiver, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Error, Result};
|
use anyhow::{Context, Error, Result};
|
||||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
app::AppConfig,
|
app::AppConfig,
|
||||||
diff::diff_objs,
|
diff::diff_objs,
|
||||||
elf,
|
elf,
|
||||||
jobs::{queue_job, update_status, Job, JobResult, JobState, Status},
|
jobs::{Job, JobResult, JobState, queue_job, Status, update_status},
|
||||||
obj::ObjInfo,
|
obj::ObjInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,13 +26,36 @@ pub struct BuildResult {
|
||||||
pub second_obj: Option<ObjInfo>,
|
pub second_obj: Option<ObjInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_make(cwd: &Path, arg: &Path) -> BuildStatus {
|
fn run_make(cwd: &Path, arg: &Path, config: &AppConfig) -> BuildStatus {
|
||||||
match (|| -> Result<BuildStatus> {
|
match (|| -> Result<BuildStatus> {
|
||||||
let output = Command::new("make")
|
let make = if config.custom_make.is_empty() { "make" } else { &config.custom_make };
|
||||||
.current_dir(cwd)
|
#[cfg(not(windows))]
|
||||||
.arg(arg)
|
let mut command = Command::new(make).current_dir(cwd).arg(arg);
|
||||||
.output()
|
#[cfg(windows)]
|
||||||
.context("Failed to execute build")?;
|
let mut command = {
|
||||||
|
use path_slash::PathExt;
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
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 {
|
||||||
|
command.current_dir(cwd).arg(arg);
|
||||||
|
}
|
||||||
|
command.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
|
||||||
|
command
|
||||||
|
};
|
||||||
|
let output = command.output().context("Failed to execute build")?;
|
||||||
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
||||||
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
||||||
Ok(BuildStatus {
|
Ok(BuildStatus {
|
||||||
|
@ -72,10 +95,10 @@ fn run_build(
|
||||||
src_path.strip_prefix(project_dir).context("Failed to create relative src obj path")?;
|
src_path.strip_prefix(project_dir).context("Failed to create relative src obj path")?;
|
||||||
|
|
||||||
update_status(status, format!("Building asm {}", obj_path), 0, 5, &cancel)?;
|
update_status(status, format!("Building asm {}", obj_path), 0, 5, &cancel)?;
|
||||||
let first_status = run_make(project_dir, asm_path_rel);
|
let first_status = run_make(project_dir, asm_path_rel, &config);
|
||||||
|
|
||||||
update_status(status, format!("Building src {}", obj_path), 1, 5, &cancel)?;
|
update_status(status, format!("Building src {}", obj_path), 1, 5, &cancel)?;
|
||||||
let second_status = run_make(project_dir, src_path_rel);
|
let second_status = run_make(project_dir, src_path_rel, &config);
|
||||||
|
|
||||||
let mut first_obj = if first_status.success {
|
let mut first_obj = if first_status.success {
|
||||||
update_status(status, format!("Loading asm {}", obj_path), 2, 5, &cancel)?;
|
update_status(status, format!("Loading asm {}", obj_path), 2, 5, &cancel)?;
|
||||||
|
|
|
@ -1,13 +1,53 @@
|
||||||
|
use std::process::Command;
|
||||||
|
use std::string::FromUtf16Error;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfig, DiffKind, ViewState},
|
app::{AppConfig, DiffKind, ViewState},
|
||||||
jobs::{bindiff::queue_bindiff, build::queue_build},
|
jobs::{bindiff::queue_bindiff, build::queue_build},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn process_utf16(bytes: &[u8]) -> Result<String, FromUtf16Error> {
|
||||||
|
let u16_bytes: Vec<u16> = bytes
|
||||||
|
.chunks_exact(2)
|
||||||
|
.filter_map(|c| Some(u16::from_ne_bytes(c.try_into().ok()?)))
|
||||||
|
.collect();
|
||||||
|
String::from_utf16(&u16_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn wsl_cmd(args: &[&str]) -> Result<String> {
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
let output = Command::new("wsl")
|
||||||
|
.args(args)
|
||||||
|
.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW)
|
||||||
|
.output()
|
||||||
|
.context("Failed to execute wsl")?;
|
||||||
|
process_utf16(&output.stdout).context("Failed to process stdout")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn fetch_wsl2_distros() -> Vec<String> {
|
||||||
|
wsl_cmd(&["-l", "-q"])
|
||||||
|
.map(|stdout| {
|
||||||
|
stdout
|
||||||
|
.split('\n')
|
||||||
|
.filter(|s| !s.trim().is_empty())
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
||||||
let mut config_guard = config.write().unwrap();
|
let mut config_guard = config.write().unwrap();
|
||||||
let AppConfig {
|
let AppConfig {
|
||||||
|
custom_make,
|
||||||
|
available_wsl_distros,
|
||||||
|
selected_wsl_distro,
|
||||||
project_dir,
|
project_dir,
|
||||||
project_dir_change,
|
project_dir_change,
|
||||||
build_asm_dir,
|
build_asm_dir,
|
||||||
|
@ -17,6 +57,30 @@ pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state:
|
||||||
right_obj,
|
right_obj,
|
||||||
} = &mut *config_guard;
|
} = &mut *config_guard;
|
||||||
|
|
||||||
|
ui.heading("Build config");
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
if available_wsl_distros.is_none() {
|
||||||
|
*available_wsl_distros = Some(fetch_wsl2_distros());
|
||||||
|
}
|
||||||
|
egui::ComboBox::from_label("Run in WSL2")
|
||||||
|
.selected_text(selected_wsl_distro.as_ref().unwrap_or(&"None".to_string()))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.selectable_value(selected_wsl_distro, None, "None");
|
||||||
|
for distro in available_wsl_distros.as_ref().unwrap() {
|
||||||
|
ui.selectable_value(selected_wsl_distro, Some(distro.clone()), distro);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.label("Custom make program:");
|
||||||
|
ui.text_edit_singleline(custom_make);
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
ui.heading("Project config");
|
||||||
|
|
||||||
if view_state.diff_kind == DiffKind::SplitObj {
|
if view_state.diff_kind == DiffKind::SplitObj {
|
||||||
if ui.button("Select project dir").clicked() {
|
if ui.button("Select project dir").clicked() {
|
||||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||||
|
|
Loading…
Reference in New Issue