Skip to content

Commit 6b8e469

Browse files
committed
Project configuration fixes & improvements
- Allow config to specify object "target_path" and "base_path" explicitly, rather than relying on relative path from the "target_dir" and "base_dir". Useful for more complex directory layouts. - Fix watch_patterns in project config not using default. - Fix "Rebuild on changes" not defaulting to true. - Keep watching project config updates even when "Rebuild on changes" is false. - Disable some configuration options when loaded from project config file.
1 parent bf3ba48 commit 6b8e469

File tree

9 files changed

+269
-163
lines changed

9 files changed

+269
-163
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "objdiff"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
edition = "2021"
55
rust-version = "1.65"
66
authors = ["Luke Street <[email protected]>"]
@@ -27,7 +27,7 @@ byteorder = "1.4.3"
2727
bytes = "1.4.0"
2828
cfg-if = "1.0.0"
2929
const_format = "0.2.31"
30-
cwdemangle = "0.1.5"
30+
cwdemangle = "0.1.6"
3131
dirs = "5.0.1"
3232
eframe = { version = "0.22.0", features = ["persistence"] }
3333
egui = "0.22.0"

README.md

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it
5050
// objdiff.json
5151
{
5252
"custom_make": "ninja",
53+
54+
// Only required if objects use "path" instead of "target_path" and "base_path".
5355
"target_dir": "build/asm",
5456
"base_dir": "build/src",
57+
5558
"build_target": true,
5659
"watch_patterns": [
5760
"*.c",
@@ -63,34 +66,59 @@ file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it
6366
],
6467
"objects": [
6568
{
69+
"name": "main/MetroTRK/mslsupp",
70+
71+
// Option 1: Relative to target_dir and base_dir
6672
"path": "MetroTRK/mslsupp.o",
67-
"name": "MetroTRK/mslsupp",
73+
// Option 2: Explicit paths from project root
74+
// Useful for more complex directory layouts
75+
"target_path": "build/asm/MetroTRK/mslsupp.o",
76+
"base_path": "build/src/MetroTRK/mslsupp.o",
77+
6878
"reverse_fn_order": false
6979
},
7080
// ...
7181
]
7282
}
7383
```
7484

75-
- `custom_make` _(optional)_: By default, objdiff will use `make` to build the project.
76-
If the project uses a different build system (e.g. `ninja`), specify it here.
77-
- `target_dir`: Relative from the root of the project, this where the "target" or "expected" objects are located.
78-
These are the **intended result** of the match.
79-
- `base_dir`: Relative from the root of the project, this is where the "base" or "actual" objects are located.
80-
These are objects built from the **current source code**.
81-
- `build_target`: If true, objdiff will tell the build system to build the target objects before diffing (e.g.
85+
`custom_make` _(optional)_: By default, objdiff will use `make` to build the project.
86+
If the project uses a different build system (e.g. `ninja`), specify it here.
87+
88+
`target_dir` _(optional)_: Relative from the root of the project, this where the "target" or "expected" objects are located.
89+
These are the **intended result** of the match.
90+
91+
`base_dir` _(optional)_: Relative from the root of the project, this is where the "base" or "actual" objects are located.
92+
These are objects built from the **current source code**.
93+
94+
`build_target`: If true, objdiff will tell the build system to build the target objects before diffing (e.g.
8295
`make path/to/target.o`).
83-
This is useful if the target objects are not built by default or can change based on project configuration or edits
84-
to assembly files.
85-
Requires the build system to be configured properly.
86-
- `watch_patterns` _(optional)_: A list of glob patterns to watch for changes.
87-
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
88-
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
89-
- `objects` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
90-
- `path`: Relative path to the object from the `target_dir` and `base_dir`.
91-
- `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
92-
- `reverse_fn_order` _(optional)_: Displays function symbols in reversed order.
93-
Used to support MWCC's `-inline deferred` option, which reverses the order of functions in the object file.
96+
This is useful if the target objects are not built by default or can change based on project configuration or edits
97+
to assembly files.
98+
Requires the build system to be configured properly.
99+
100+
`watch_patterns` _(optional)_: A list of glob patterns to watch for changes.
101+
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
102+
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
103+
If not specified, objdiff will use the default patterns listed above.
104+
105+
`objects` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
106+
107+
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
108+
>
109+
> `path`: Relative path to the object from the `target_dir` and `base_dir`.
110+
> Requires `target_dir` and `base_dir` to be specified.
111+
>
112+
> `target_path`: Path to the target object from the project root.
113+
> Required if `path` is not specified.
114+
>
115+
> `base_path`: Path to the base object from the project root.
116+
> Required if `path` is not specified.
117+
>
118+
> `reverse_fn_order` _(optional)_: Displays function symbols in reversed order.
119+
Used to support MWCC's `-inline deferred` option, which reverses the order of functions in the object file.
120+
121+
94122

95123
## License
96124

src/app.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,29 @@ pub struct ViewState {
4040
pub show_project_config: bool,
4141
}
4242

43+
/// The configuration for a single object file.
44+
#[derive(Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
45+
pub struct ObjectConfig {
46+
pub name: String,
47+
pub target_path: PathBuf,
48+
pub base_path: PathBuf,
49+
pub reverse_fn_order: Option<bool>,
50+
}
51+
52+
#[inline]
53+
fn bool_true() -> bool { true }
54+
4355
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
4456
pub struct AppConfig {
4557
pub custom_make: Option<String>,
4658
pub selected_wsl_distro: Option<String>,
4759
pub project_dir: Option<PathBuf>,
4860
pub target_obj_dir: Option<PathBuf>,
4961
pub base_obj_dir: Option<PathBuf>,
50-
pub obj_path: Option<String>,
62+
pub selected_obj: Option<ObjectConfig>,
5163
pub build_target: bool,
52-
pub watcher_enabled: bool,
64+
#[serde(default = "bool_true")]
65+
pub rebuild_on_changes: bool,
5366
pub auto_update_check: bool,
5467
pub watch_patterns: Vec<Glob>,
5568

@@ -65,39 +78,42 @@ pub struct AppConfig {
6578
pub obj_change: bool,
6679
#[serde(skip)]
6780
pub queue_build: bool,
81+
#[serde(skip)]
82+
pub project_config_loaded: bool,
6883
}
6984

7085
impl AppConfig {
7186
pub fn set_project_dir(&mut self, path: PathBuf) {
7287
self.project_dir = Some(path);
7388
self.target_obj_dir = None;
7489
self.base_obj_dir = None;
75-
self.obj_path = None;
90+
self.selected_obj = None;
7691
self.build_target = false;
7792
self.objects.clear();
7893
self.object_nodes.clear();
7994
self.watcher_change = true;
8095
self.config_change = true;
8196
self.obj_change = true;
8297
self.queue_build = false;
98+
self.project_config_loaded = false;
8399
}
84100

85101
pub fn set_target_obj_dir(&mut self, path: PathBuf) {
86102
self.target_obj_dir = Some(path);
87-
self.obj_path = None;
103+
self.selected_obj = None;
88104
self.obj_change = true;
89105
self.queue_build = false;
90106
}
91107

92108
pub fn set_base_obj_dir(&mut self, path: PathBuf) {
93109
self.base_obj_dir = Some(path);
94-
self.obj_path = None;
110+
self.selected_obj = None;
95111
self.obj_change = true;
96112
self.queue_build = false;
97113
}
98114

99-
pub fn set_obj_path(&mut self, path: String) {
100-
self.obj_path = Some(path);
115+
pub fn set_selected_obj(&mut self, object: ObjectConfig) {
116+
self.selected_obj = Some(object);
101117
self.obj_change = true;
102118
self.queue_build = false;
103119
}
@@ -258,19 +274,19 @@ impl App {
258274

259275
if config.obj_change {
260276
*diff_state = Default::default();
261-
if config.obj_path.is_some() {
277+
if config.selected_obj.is_some() {
262278
config.queue_build = true;
263279
}
264280
config.obj_change = false;
265281
}
266282

267-
if self.modified.swap(false, Ordering::Relaxed) {
283+
if self.modified.swap(false, Ordering::Relaxed) && config.rebuild_on_changes {
268284
config.queue_build = true;
269285
}
270286

271287
// Don't clear `queue_build` if a build is running. A file may have been modified during
272288
// the build, so we'll start another build after the current one finishes.
273-
if config.queue_build && config.obj_path.is_some() && !jobs.is_running(Job::ObjDiff) {
289+
if config.queue_build && config.selected_obj.is_some() && !jobs.is_running(Job::ObjDiff) {
274290
jobs.push(start_build(self.config.clone()));
275291
config.queue_build = false;
276292
}

src/config.rs

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
use anyhow::{Context, Result};
77
use globset::{Glob, GlobSet, GlobSetBuilder};
88

9-
use crate::app::AppConfig;
9+
use crate::{app::AppConfig, views::config::DEFAULT_WATCH_PATTERNS};
1010

1111
#[derive(Default, Clone, serde::Deserialize)]
1212
#[serde(default)]
@@ -15,18 +15,32 @@ pub struct ProjectConfig {
1515
pub target_dir: Option<PathBuf>,
1616
pub base_dir: Option<PathBuf>,
1717
pub build_target: bool,
18-
pub watch_patterns: Vec<Glob>,
18+
pub watch_patterns: Option<Vec<Glob>>,
1919
#[serde(alias = "units")]
2020
pub objects: Vec<ProjectObject>,
2121
}
2222

2323
#[derive(Default, Clone, serde::Deserialize)]
2424
pub struct ProjectObject {
2525
pub name: Option<String>,
26-
pub path: PathBuf,
26+
pub path: Option<PathBuf>,
27+
pub target_path: Option<PathBuf>,
28+
pub base_path: Option<PathBuf>,
2729
pub reverse_fn_order: Option<bool>,
2830
}
2931

32+
impl ProjectObject {
33+
pub fn name(&self) -> &str {
34+
if let Some(name) = &self.name {
35+
name
36+
} else if let Some(path) = &self.path {
37+
path.to_str().unwrap_or("[invalid path]")
38+
} else {
39+
"[unknown]"
40+
}
41+
}
42+
}
43+
3044
#[derive(Clone)]
3145
pub enum ProjectObjectNode {
3246
File(String, ProjectObject),
@@ -53,11 +67,22 @@ fn find_dir<'a>(
5367
unreachable!();
5468
}
5569

56-
fn build_nodes(objects: &[ProjectObject]) -> Vec<ProjectObjectNode> {
70+
fn build_nodes(
71+
objects: &[ProjectObject],
72+
project_dir: &PathBuf,
73+
target_obj_dir: &Option<PathBuf>,
74+
base_obj_dir: &Option<PathBuf>,
75+
) -> Vec<ProjectObjectNode> {
5776
let mut nodes = vec![];
5877
for object in objects {
5978
let mut out_nodes = &mut nodes;
60-
let path = object.name.as_ref().map(Path::new).unwrap_or(&object.path);
79+
let path = if let Some(name) = &object.name {
80+
Path::new(name)
81+
} else if let Some(path) = &object.path {
82+
path
83+
} else {
84+
continue;
85+
};
6186
if let Some(parent) = path.parent() {
6287
for component in parent.components() {
6388
if let Component::Normal(name) = component {
@@ -66,8 +91,23 @@ fn build_nodes(objects: &[ProjectObject]) -> Vec<ProjectObjectNode> {
6691
}
6792
}
6893
}
94+
let mut object = object.clone();
95+
if let (Some(target_obj_dir), Some(path), None) =
96+
(target_obj_dir, &object.path, &object.target_path)
97+
{
98+
object.target_path = Some(target_obj_dir.join(path));
99+
} else if let Some(path) = &object.target_path {
100+
object.target_path = Some(project_dir.join(path));
101+
}
102+
if let (Some(base_obj_dir), Some(path), None) =
103+
(base_obj_dir, &object.path, &object.base_path)
104+
{
105+
object.base_path = Some(base_obj_dir.join(path));
106+
} else if let Some(path) = &object.base_path {
107+
object.base_path = Some(project_dir.join(path));
108+
}
69109
let filename = path.file_name().unwrap().to_str().unwrap().to_string();
70-
out_nodes.push(ProjectObjectNode::File(filename, object.clone()));
110+
out_nodes.push(ProjectObjectNode::File(filename, object));
71111
}
72112
nodes
73113
}
@@ -84,10 +124,14 @@ pub fn load_project_config(config: &mut AppConfig) -> Result<()> {
84124
config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p));
85125
config.base_obj_dir = project_config.base_dir.map(|p| project_dir.join(p));
86126
config.build_target = project_config.build_target;
87-
config.watch_patterns = project_config.watch_patterns;
127+
config.watch_patterns = project_config.watch_patterns.unwrap_or_else(|| {
128+
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
129+
});
88130
config.watcher_change = true;
89131
config.objects = project_config.objects;
90-
config.object_nodes = build_nodes(&config.objects);
132+
config.object_nodes =
133+
build_nodes(&config.objects, project_dir, &config.target_obj_dir, &config.base_obj_dir);
134+
config.project_config_loaded = true;
91135
}
92136
Ok(())
93137
}

0 commit comments

Comments
 (0)