mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e202c3ef95 | |||
| b7730b3d00 | |||
| a4fdb61f04 | |||
| 2876be37a3 | |||
| 11171763eb | |||
| 6037a79ba2 | |||
| f7efe5fdff | |||
| 0692deac59 | |||
| c3e3d175c5 | |||
| c45f4bbc99 | |||
| b0c5431ac5 | |||
| 9ab246367b | |||
| dcafe51eda | |||
| c65e87c382 | |||
| 1756b9f6c5 | |||
| 303f2938a2 | |||
| 526e031251 | |||
| 10b2a9c129 | |||
| abe68ef2f2 | |||
| 304df96411 | |||
| 7aa878b48e | |||
| a119d9a6dd | |||
| ebf653816a | |||
| 424434edd6 | |||
| 7f14b684bf | |||
| c5da7f7dd5 | |||
| 2fd655850a | |||
| 79bd7317c1 | |||
| 21f8f2407c | |||
| d2b7a9ef25 | |||
| 2cf9cf24d6 | |||
| 5ef3416457 | |||
| 6ff8d002f7 | |||
| 9ca157d717 | |||
| 67b63311fc | |||
| 72ea1c8911 | |||
| d4a540857d | |||
| 676488433f | |||
| 83de98b5ee | |||
| c1ba4e91d1 | |||
| 575900024d | |||
| cbe299e859 | |||
| 741d93e211 | |||
| 603dbd6882 | |||
| 6fb0a63de2 | |||
| ab2e84a2c6 | |||
| 9596051cb4 |
@@ -0,0 +1,5 @@
|
|||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
linker = "rust-lld"
|
||||||
|
|
||||||
|
[target.aarch64-pc-windows-msvc]
|
||||||
|
linker = "rust-lld"
|
||||||
@@ -30,6 +30,8 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
|
- name: Cache Rust workspace
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
- name: Cargo check
|
- name: Cargo check
|
||||||
run: cargo check --all-features --all-targets
|
run: cargo check --all-features --all-targets
|
||||||
- name: Cargo clippy
|
- name: Cargo clippy
|
||||||
@@ -63,7 +65,7 @@ jobs:
|
|||||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||||
with:
|
with:
|
||||||
command: check ${{ matrix.checks }}
|
command: check ${{ matrix.checks }}
|
||||||
|
|
||||||
@@ -85,6 +87,8 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Cache Rust workspace
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
- name: Cargo test
|
- name: Cargo test
|
||||||
run: cargo test --release --all-features
|
run: cargo test --release --all-features
|
||||||
|
|
||||||
@@ -142,11 +146,19 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install cargo-zigbuild
|
- name: Install cargo-zigbuild
|
||||||
if: matrix.build == 'zigbuild'
|
if: matrix.build == 'zigbuild'
|
||||||
run: pip install ziglang==0.13.0 cargo-zigbuild==0.19.1
|
run: |
|
||||||
|
python3 -m venv .venv
|
||||||
|
. .venv/bin/activate
|
||||||
|
echo PATH=$PATH >> $GITHUB_ENV
|
||||||
|
pip install ziglang==0.13.0 cargo-zigbuild==0.19.1
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
|
- name: Cache Rust workspace
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
key: ${{ matrix.target }}
|
||||||
- name: Cargo build
|
- name: Cargo build
|
||||||
run: >
|
run: >
|
||||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
@@ -198,6 +210,10 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
|
- name: Cache Rust workspace
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
key: ${{ matrix.target }}
|
||||||
- name: Cargo build
|
- name: Cargo build
|
||||||
run: >
|
run: >
|
||||||
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
|
|||||||
+1
-5
@@ -3,10 +3,6 @@ target/
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
generated/
|
generated/
|
||||||
|
|
||||||
# cargo-mobile
|
|
||||||
.cargo/
|
|
||||||
/gen
|
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
@@ -22,4 +18,4 @@ android.keystore
|
|||||||
*.frag
|
*.frag
|
||||||
*.vert
|
*.vert
|
||||||
*.metal
|
*.metal
|
||||||
.vscode/launch.json
|
.vscode/
|
||||||
|
|||||||
Generated
+1094
-746
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -13,9 +13,9 @@ strip = "debuginfo"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "2.2.2"
|
version = "2.7.1"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/encounter/objdiff"
|
repository = "https://github.com/encounter/objdiff"
|
||||||
rust-version = "1.74"
|
rust-version = "1.81"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ Supports:
|
|||||||
- MIPS (N64, PS1, PS2, PSP)
|
- MIPS (N64, PS1, PS2, PSP)
|
||||||
- x86 (COFF only at the moment)
|
- x86 (COFF only at the moment)
|
||||||
- ARM (GBA, DS, 3DS)
|
- ARM (GBA, DS, 3DS)
|
||||||
|
- ARM64 (Switch, experimental)
|
||||||
|
|
||||||
See [Usage](#usage) for more information.
|
See [Usage](#usage) for more information.
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,13 @@
|
|||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ref": "#/$defs/metadata"
|
"ref": "#/$defs/metadata"
|
||||||
|
},
|
||||||
|
"symbol_mappings": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Manual symbol mappings from target to base.",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ feature-depth = 1
|
|||||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||||
# output a note when they are encountered.
|
# output a note when they are encountered.
|
||||||
ignore = [
|
ignore = [
|
||||||
"RUSTSEC-2024-0370",
|
|
||||||
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
|
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
|
||||||
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
|
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
|
||||||
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
|
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
|
||||||
@@ -98,7 +97,7 @@ allow = [
|
|||||||
"BSL-1.0",
|
"BSL-1.0",
|
||||||
"CC0-1.0",
|
"CC0-1.0",
|
||||||
"MPL-2.0",
|
"MPL-2.0",
|
||||||
"Unicode-DFS-2016",
|
"Unicode-3.0",
|
||||||
"Zlib",
|
"Zlib",
|
||||||
"0BSD",
|
"0BSD",
|
||||||
"OFL-1.1",
|
"OFL-1.1",
|
||||||
@@ -240,7 +239,7 @@ allow-git = []
|
|||||||
|
|
||||||
[sources.allow-org]
|
[sources.allow-org]
|
||||||
# github.com organizations to allow git sources for
|
# github.com organizations to allow git sources for
|
||||||
github = ["encounter"]
|
github = []
|
||||||
# gitlab.com organizations to allow git sources for
|
# gitlab.com organizations to allow git sources for
|
||||||
gitlab = []
|
gitlab = []
|
||||||
# bitbucket.org organizations to allow git sources for
|
# bitbucket.org organizations to allow git sources for
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
argp = "0.3"
|
argp = "0.4"
|
||||||
crossterm = "0.28"
|
crossterm = "0.28"
|
||||||
enable-ansi-support = "0.2"
|
enable-ansi-support = "0.2"
|
||||||
memmap2 = "0.9"
|
memmap2 = "0.9"
|
||||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||||
prost = "0.13"
|
prost = "0.13"
|
||||||
ratatui = "0.28"
|
ratatui = "0.29"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|||||||
+257
-759
File diff suppressed because it is too large
Load Diff
@@ -94,7 +94,7 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
|||||||
};
|
};
|
||||||
info!(
|
info!(
|
||||||
"Generating report for {} units (using {} threads)",
|
"Generating report for {} units (using {} threads)",
|
||||||
project.objects.len(),
|
project.units().len(),
|
||||||
if args.deduplicate { 1 } else { rayon::current_num_threads() }
|
if args.deduplicate { 1 } else { rayon::current_num_threads() }
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
|||||||
let mut existing_functions: HashSet<String> = HashSet::new();
|
let mut existing_functions: HashSet<String> = HashSet::new();
|
||||||
if args.deduplicate {
|
if args.deduplicate {
|
||||||
// If deduplicating, we need to run single-threaded
|
// If deduplicating, we need to run single-threaded
|
||||||
for object in &mut project.objects {
|
for object in project.units.as_deref_mut().unwrap_or_default() {
|
||||||
if let Some(unit) = report_object(
|
if let Some(unit) = report_object(
|
||||||
object,
|
object,
|
||||||
project_dir,
|
project_dir,
|
||||||
@@ -116,7 +116,9 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let vec = project
|
let vec = project
|
||||||
.objects
|
.units
|
||||||
|
.as_deref_mut()
|
||||||
|
.unwrap_or_default()
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.map(|object| {
|
.map(|object| {
|
||||||
report_object(
|
report_object(
|
||||||
@@ -132,7 +134,7 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
|||||||
}
|
}
|
||||||
let measures = units.iter().flat_map(|u| u.measures.into_iter()).collect();
|
let measures = units.iter().flat_map(|u| u.measures.into_iter()).collect();
|
||||||
let mut categories = Vec::new();
|
let mut categories = Vec::new();
|
||||||
for category in &project.progress_categories {
|
for category in project.progress_categories() {
|
||||||
categories.push(ReportCategory {
|
categories.push(ReportCategory {
|
||||||
id: category.id.clone(),
|
id: category.id.clone(),
|
||||||
name: category.name.clone(),
|
name: category.name.clone(),
|
||||||
@@ -167,22 +169,29 @@ fn report_object(
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
let diff_config = diff::DiffObjConfig {
|
||||||
|
function_reloc_diffs: diff::FunctionRelocDiffs::None,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mapping_config = diff::MappingConfig::default();
|
||||||
let target = object
|
let target = object
|
||||||
.target_path
|
.target_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
obj::read::read(p, &diff_config)
|
||||||
|
.with_context(|| format!("Failed to open {}", p.display()))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let base = object
|
let base = object
|
||||||
.base_path
|
.base_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
obj::read::read(p, &diff_config)
|
||||||
|
.with_context(|| format!("Failed to open {}", p.display()))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
|
let result =
|
||||||
|
diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?;
|
||||||
|
|
||||||
let metadata = ReportUnitMetadata {
|
let metadata = ReportUnitMetadata {
|
||||||
complete: object.complete(),
|
complete: object.complete(),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod argp_version;
|
mod argp_version;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod views;
|
||||||
|
|
||||||
// musl's allocator is very slow, so use mimalloc when targeting musl.
|
// musl's allocator is very slow, so use mimalloc when targeting musl.
|
||||||
// Otherwise, use the system allocator to avoid extra code size.
|
// Otherwise, use the system allocator to avoid extra code size.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use crossterm::event::Event;
|
||||||
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
use crate::cmd::diff::AppState;
|
||||||
|
|
||||||
|
pub mod function_diff;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EventResult {
|
||||||
|
pub redraw: bool,
|
||||||
|
pub click_xy: Option<(u16, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum EventControlFlow {
|
||||||
|
Break,
|
||||||
|
Continue(EventResult),
|
||||||
|
Reload,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UiView {
|
||||||
|
fn draw(&mut self, state: &AppState, f: &mut Frame, result: &mut EventResult);
|
||||||
|
fn handle_event(&mut self, state: &mut AppState, event: Event) -> EventControlFlow;
|
||||||
|
fn reload(&mut self, state: &AppState) -> Result<()>;
|
||||||
|
}
|
||||||
+145
-24
@@ -16,36 +16,125 @@ documentation = "https://docs.rs/objdiff-core"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
all = ["config", "dwarf", "mips", "ppc", "x86", "arm", "bindings"]
|
all = [
|
||||||
any-arch = [] # Implicit, used to check if any arch is enabled
|
# Features
|
||||||
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
"bindings",
|
||||||
dwarf = ["gimli"]
|
"build",
|
||||||
mips = ["any-arch", "rabbitizer"]
|
"config",
|
||||||
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
"dwarf",
|
||||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
# Architectures
|
||||||
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
"mips",
|
||||||
bindings = ["serde_json", "prost", "pbjson"]
|
"ppc",
|
||||||
wasm = ["bindings", "console_error_panic_hook", "console_log"]
|
"x86",
|
||||||
|
"arm",
|
||||||
|
"arm64",
|
||||||
|
]
|
||||||
|
# Implicit, used to check if any arch is enabled
|
||||||
|
any-arch = [
|
||||||
|
"config",
|
||||||
|
"dep:bimap",
|
||||||
|
"dep:byteorder",
|
||||||
|
"dep:flagset",
|
||||||
|
"dep:heck",
|
||||||
|
"dep:log",
|
||||||
|
"dep:memmap2",
|
||||||
|
"dep:num-traits",
|
||||||
|
"dep:prettyplease",
|
||||||
|
"dep:proc-macro2",
|
||||||
|
"dep:quote",
|
||||||
|
"dep:serde",
|
||||||
|
"dep:serde_json",
|
||||||
|
"dep:similar",
|
||||||
|
"dep:strum",
|
||||||
|
"dep:syn",
|
||||||
|
]
|
||||||
|
bindings = [
|
||||||
|
"dep:pbjson",
|
||||||
|
"dep:pbjson-build",
|
||||||
|
"dep:prost",
|
||||||
|
"dep:prost-build",
|
||||||
|
"dep:serde",
|
||||||
|
"dep:serde_json",
|
||||||
|
]
|
||||||
|
build = [
|
||||||
|
"dep:notify",
|
||||||
|
"dep:notify-debouncer-full",
|
||||||
|
"dep:path-slash",
|
||||||
|
"dep:reqwest",
|
||||||
|
"dep:self_update",
|
||||||
|
"dep:shell-escape",
|
||||||
|
"dep:tempfile",
|
||||||
|
"dep:time",
|
||||||
|
"dep:winapi",
|
||||||
|
]
|
||||||
|
config = [
|
||||||
|
"dep:bimap",
|
||||||
|
"dep:filetime",
|
||||||
|
"dep:globset",
|
||||||
|
"dep:semver",
|
||||||
|
"dep:serde",
|
||||||
|
"dep:serde_json",
|
||||||
|
"dep:serde_yaml",
|
||||||
|
]
|
||||||
|
dwarf = ["dep:gimli"]
|
||||||
|
mips = [
|
||||||
|
"any-arch",
|
||||||
|
"dep:rabbitizer",
|
||||||
|
]
|
||||||
|
ppc = [
|
||||||
|
"any-arch",
|
||||||
|
"dep:cwdemangle",
|
||||||
|
"dep:cwextab",
|
||||||
|
"dep:ppc750cl",
|
||||||
|
]
|
||||||
|
x86 = [
|
||||||
|
"any-arch",
|
||||||
|
"dep:cpp_demangle",
|
||||||
|
"dep:iced-x86",
|
||||||
|
"dep:msvc-demangler",
|
||||||
|
]
|
||||||
|
arm = [
|
||||||
|
"any-arch",
|
||||||
|
"dep:arm-attr",
|
||||||
|
"dep:cpp_demangle",
|
||||||
|
"dep:unarm",
|
||||||
|
]
|
||||||
|
arm64 = [
|
||||||
|
"any-arch",
|
||||||
|
"dep:cpp_demangle",
|
||||||
|
"dep:yaxpeax-arch",
|
||||||
|
"dep:yaxpeax-arm",
|
||||||
|
]
|
||||||
|
wasm = [
|
||||||
|
"any-arch",
|
||||||
|
"bindings",
|
||||||
|
"dep:console_error_panic_hook",
|
||||||
|
"dep:console_log",
|
||||||
|
"dep:log",
|
||||||
|
"dep:tsify-next",
|
||||||
|
"dep:wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["all"]
|
features = ["all"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
byteorder = "1.5"
|
bimap = { version = "0.6", features = ["serde"], optional = true }
|
||||||
filetime = "0.2"
|
byteorder = { version = "1.5", optional = true }
|
||||||
flagset = "0.4"
|
filetime = { version = "0.2", optional = true }
|
||||||
log = "0.4"
|
flagset = { version = "0.4", optional = true }
|
||||||
memmap2 = "0.9"
|
log = { version = "0.4", optional = true }
|
||||||
num-traits = "0.2"
|
memmap2 = { version = "0.9", optional = true }
|
||||||
|
num-traits = { version = "0.2", optional = true }
|
||||||
object = { version = "0.36", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
object = { version = "0.36", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
||||||
pbjson = { version = "0.7", optional = true }
|
pbjson = { version = "0.7", optional = true }
|
||||||
prost = { version = "0.13", optional = true }
|
prost = { version = "0.13", optional = true }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
similar = { version = "2.6", default-features = false }
|
similar = { version = "2.6", default-features = false, optional = true }
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"], optional = true }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = { version = "0.2", optional = true }
|
||||||
tsify-next = { version = "0.5", default-features = false, features = ["js"] }
|
tsify-next = { version = "0.5", default-features = false, features = ["js"], optional = true }
|
||||||
console_log = { version = "1.0", optional = true }
|
console_log = { version = "1.0", optional = true }
|
||||||
console_error_panic_hook = { version = "0.1", optional = true }
|
console_error_panic_hook = { version = "0.1", optional = true }
|
||||||
|
|
||||||
@@ -60,7 +149,7 @@ gimli = { version = "0.31", default-features = false, features = ["read-all"], o
|
|||||||
|
|
||||||
# ppc
|
# ppc
|
||||||
cwdemangle = { version = "1.0", optional = true }
|
cwdemangle = { version = "1.0", optional = true }
|
||||||
cwextab = { version = "1.0.2", optional = true }
|
cwextab = { version = "1.0", optional = true }
|
||||||
ppc750cl = { version = "0.3", optional = true }
|
ppc750cl = { version = "0.3", optional = true }
|
||||||
|
|
||||||
# mips
|
# mips
|
||||||
@@ -75,6 +164,38 @@ msvc-demangler = { version = "0.10", optional = true }
|
|||||||
unarm = { version = "1.6", optional = true }
|
unarm = { version = "1.6", optional = true }
|
||||||
arm-attr = { version = "0.1", optional = true }
|
arm-attr = { version = "0.1", optional = true }
|
||||||
|
|
||||||
|
# arm64
|
||||||
|
yaxpeax-arch = { version = "0.3", default-features = false, features = ["std"], optional = true }
|
||||||
|
yaxpeax-arm = { version = "0.3", default-features = false, features = ["std"], optional = true }
|
||||||
|
|
||||||
|
# build
|
||||||
|
notify = { version = "8.0.0", optional = true }
|
||||||
|
notify-debouncer-full = { version = "0.5.0", optional = true }
|
||||||
|
shell-escape = { version = "0.1", optional = true }
|
||||||
|
tempfile = { version = "3.15", optional = true }
|
||||||
|
time = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
path-slash = { version = "0.2", optional = true }
|
||||||
|
winapi = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
# For Linux static binaries, use rustls
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "multipart", "rustls-tls"], optional = true }
|
||||||
|
self_update = { version = "0.42", default-features = false, features = ["rustls"], optional = true }
|
||||||
|
|
||||||
|
# For all other platforms, use native TLS
|
||||||
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||||
|
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "multipart", "default-tls"], optional = true }
|
||||||
|
self_update = { version = "0.42", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
prost-build = "0.13"
|
heck = { version = "0.5", optional = true }
|
||||||
pbjson-build = "0.7"
|
pbjson-build = { version = "0.7", optional = true }
|
||||||
|
prettyplease = { version = "0.2", optional = true }
|
||||||
|
proc-macro2 = { version = "1.0", optional = true }
|
||||||
|
prost-build = { version = "0.13", optional = true }
|
||||||
|
quote = { version = "1.0", optional = true }
|
||||||
|
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
syn = { version = "2.0", optional = true }
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
|
|||||||
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
||||||
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
|
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
|
||||||
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
||||||
|
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
|
||||||
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
||||||
|
|||||||
+11
-1
@@ -1,6 +1,16 @@
|
|||||||
use std::path::{Path, PathBuf};
|
#[cfg(feature = "any-arch")]
|
||||||
|
mod config_gen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(feature = "bindings")]
|
||||||
|
compile_protos();
|
||||||
|
#[cfg(feature = "any-arch")]
|
||||||
|
config_gen::generate_diff_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bindings")]
|
||||||
|
fn compile_protos() {
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("protos");
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("protos");
|
||||||
let descriptor_path = root.join("proto_descriptor.bin");
|
let descriptor_path = root.join("proto_descriptor.bin");
|
||||||
println!("cargo:rerun-if-changed={}", descriptor_path.display());
|
println!("cargo:rerun-if-changed={}", descriptor_path.display());
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "functionRelocDiffs",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "name_address",
|
||||||
|
"name": "Function relocation diffs",
|
||||||
|
"description": "How relocation targets will be diffed in the function view.",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "none",
|
||||||
|
"name": "None"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "name_address",
|
||||||
|
"name": "Name or address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "data_value",
|
||||||
|
"name": "Data value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "all",
|
||||||
|
"name": "Name or address, data value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "spaceBetweenArgs",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"name": "Space between args",
|
||||||
|
"description": "Adds a space between arguments in the diff output."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "combineDataSections",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Combine data sections",
|
||||||
|
"description": "Combines data sections with equal names."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.archVersion",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "auto",
|
||||||
|
"name": "Architecture version",
|
||||||
|
"description": "ARM architecture version to use for disassembly.",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "auto",
|
||||||
|
"name": "Auto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "v4t",
|
||||||
|
"name": "ARMv4T (GBA)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "v5te",
|
||||||
|
"name": "ARMv5TE (DS)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "v6k",
|
||||||
|
"name": "ARMv6K (3DS)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.unifiedSyntax",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Unified syntax",
|
||||||
|
"description": "Disassemble as unified assembly language (UAL)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.avRegisters",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Use A/V registers",
|
||||||
|
"description": "Display R0-R3 as A1-A4 and R4-R11 as V1-V8."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.r9Usage",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "generalPurpose",
|
||||||
|
"name": "Display R9 as",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "generalPurpose",
|
||||||
|
"name": "R9 or V6",
|
||||||
|
"description": "Use R9 as a general-purpose register."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "sb",
|
||||||
|
"name": "SB (static base)",
|
||||||
|
"description": "Used for position-independent data (PID)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "tr",
|
||||||
|
"name": "TR (TLS register)",
|
||||||
|
"description": "Used for thread-local storage."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.slUsage",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Display R10 as SL",
|
||||||
|
"description": "Used for explicit stack limits."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.fpUsage",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Display R11 as FP",
|
||||||
|
"description": "Used for frame pointers."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm.ipUsage",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Display R12 as IP",
|
||||||
|
"description": "Used for interworking and long branches."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mips.abi",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "auto",
|
||||||
|
"name": "ABI",
|
||||||
|
"description": "MIPS ABI to use for disassembly.",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "auto",
|
||||||
|
"name": "Auto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "o32",
|
||||||
|
"name": "O32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "n32",
|
||||||
|
"name": "N32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "n64",
|
||||||
|
"name": "N64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mips.instrCategory",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "auto",
|
||||||
|
"name": "Instruction category",
|
||||||
|
"description": "MIPS instruction category to use for disassembly.",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "auto",
|
||||||
|
"name": "Auto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "cpu",
|
||||||
|
"name": "CPU"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "rsp",
|
||||||
|
"name": "RSP (N64)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "r3000gte",
|
||||||
|
"name": "R3000 GTE (PS1)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "r4000allegrex",
|
||||||
|
"name": "R4000 ALLEGREX (PSP)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "r5900",
|
||||||
|
"name": "R5900 EE (PS2)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "x86.formatter",
|
||||||
|
"type": "choice",
|
||||||
|
"default": "intel",
|
||||||
|
"name": "Format",
|
||||||
|
"description": "x86 disassembly syntax.",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "intel",
|
||||||
|
"name": "Intel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "gas",
|
||||||
|
"name": "AT&T"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "nasm",
|
||||||
|
"name": "NASM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "masm",
|
||||||
|
"name": "MASM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "general",
|
||||||
|
"name": "General",
|
||||||
|
"properties": [
|
||||||
|
"functionRelocDiffs",
|
||||||
|
"spaceBetweenArgs",
|
||||||
|
"combineDataSections"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arm",
|
||||||
|
"name": "ARM",
|
||||||
|
"properties": [
|
||||||
|
"arm.archVersion",
|
||||||
|
"arm.unifiedSyntax",
|
||||||
|
"arm.avRegisters",
|
||||||
|
"arm.r9Usage",
|
||||||
|
"arm.slUsage",
|
||||||
|
"arm.fpUsage",
|
||||||
|
"arm.ipUsage"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mips",
|
||||||
|
"name": "MIPS",
|
||||||
|
"properties": [
|
||||||
|
"mips.abi",
|
||||||
|
"mips.instrCategory"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "x86",
|
||||||
|
"name": "x86",
|
||||||
|
"properties": [
|
||||||
|
"x86.formatter"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,491 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct ConfigSchema {
|
||||||
|
pub properties: Vec<ConfigProperty>,
|
||||||
|
pub groups: Vec<ConfigGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum ConfigProperty {
|
||||||
|
#[serde(rename = "boolean")]
|
||||||
|
Boolean(ConfigPropertyBoolean),
|
||||||
|
#[serde(rename = "choice")]
|
||||||
|
Choice(ConfigPropertyChoice),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct ConfigPropertyBase {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct ConfigPropertyBoolean {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub base: ConfigPropertyBase,
|
||||||
|
pub default: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct ConfigPropertyChoice {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub base: ConfigPropertyBase,
|
||||||
|
pub default: String,
|
||||||
|
pub items: Vec<ConfigPropertyChoiceItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct ConfigPropertyChoiceItem {
|
||||||
|
pub value: String,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
pub struct ConfigGroup {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub properties: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_doc(name: &str, description: Option<&str>) -> TokenStream {
|
||||||
|
let mut doc = format!(" {}", name);
|
||||||
|
let mut out = quote! { #[doc = #doc] };
|
||||||
|
if let Some(description) = description {
|
||||||
|
doc = format!(" {}", description);
|
||||||
|
out.extend(quote! { #[doc = ""] });
|
||||||
|
out.extend(quote! { #[doc = #doc] });
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_diff_config() {
|
||||||
|
let schema_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config-schema.json");
|
||||||
|
println!("cargo:rerun-if-changed={}", schema_path.display());
|
||||||
|
let schema_file = File::open(schema_path).expect("Failed to open config schema file");
|
||||||
|
let schema: ConfigSchema =
|
||||||
|
serde_json::from_reader(schema_file).expect("Failed to parse config schema");
|
||||||
|
|
||||||
|
let mut enums = TokenStream::new();
|
||||||
|
for property in &schema.properties {
|
||||||
|
let ConfigProperty::Choice(choice) = property else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let enum_ident = format_ident!("{}", choice.base.id.to_upper_camel_case());
|
||||||
|
let mut variants = TokenStream::new();
|
||||||
|
let mut full_variants = TokenStream::new();
|
||||||
|
let mut variant_info = TokenStream::new();
|
||||||
|
let mut variant_to_str = TokenStream::new();
|
||||||
|
let mut variant_to_name = TokenStream::new();
|
||||||
|
let mut variant_to_description = TokenStream::new();
|
||||||
|
let mut variant_from_str = TokenStream::new();
|
||||||
|
for item in &choice.items {
|
||||||
|
let variant_name = item.value.to_upper_camel_case();
|
||||||
|
let variant_ident = format_ident!("{}", variant_name);
|
||||||
|
let is_default = item.value == choice.default;
|
||||||
|
variants.extend(build_doc(&item.name, item.description.as_deref()));
|
||||||
|
if is_default {
|
||||||
|
variants.extend(quote! { #[default] });
|
||||||
|
}
|
||||||
|
let value = &item.value;
|
||||||
|
variants.extend(quote! {
|
||||||
|
#[serde(rename = #value, alias = #variant_name)]
|
||||||
|
#variant_ident,
|
||||||
|
});
|
||||||
|
full_variants.extend(quote! { #enum_ident::#variant_ident, });
|
||||||
|
variant_to_str.extend(quote! { #enum_ident::#variant_ident => #value, });
|
||||||
|
let name = &item.name;
|
||||||
|
variant_to_name.extend(quote! { #enum_ident::#variant_ident => #name, });
|
||||||
|
if let Some(description) = &item.description {
|
||||||
|
variant_to_description.extend(quote! {
|
||||||
|
#enum_ident::#variant_ident => Some(#description),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
variant_to_description.extend(quote! {
|
||||||
|
#enum_ident::#variant_ident => None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let description = if let Some(description) = &item.description {
|
||||||
|
quote! { Some(#description) }
|
||||||
|
} else {
|
||||||
|
quote! { None }
|
||||||
|
};
|
||||||
|
variant_info.extend(quote! {
|
||||||
|
ConfigEnumVariantInfo {
|
||||||
|
value: #value,
|
||||||
|
name: #name,
|
||||||
|
description: #description,
|
||||||
|
is_default: #is_default,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
variant_from_str.extend(quote! {
|
||||||
|
if s.eq_ignore_ascii_case(#value) { return Ok(#enum_ident::#variant_ident); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
enums.extend(quote! {
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||||
|
pub enum #enum_ident {
|
||||||
|
#variants
|
||||||
|
}
|
||||||
|
impl ConfigEnum for #enum_ident {
|
||||||
|
#[inline]
|
||||||
|
fn variants() -> &'static [Self] {
|
||||||
|
static VARIANTS: &[#enum_ident] = &[#full_variants];
|
||||||
|
VARIANTS
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn variant_info() -> &'static [ConfigEnumVariantInfo] {
|
||||||
|
static VARIANT_INFO: &[ConfigEnumVariantInfo] = &[
|
||||||
|
#variant_info
|
||||||
|
];
|
||||||
|
VARIANT_INFO
|
||||||
|
}
|
||||||
|
fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#variant_to_str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#variant_to_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn description(&self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
#variant_to_description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::str::FromStr for #enum_ident {
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
#variant_from_str
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut groups = TokenStream::new();
|
||||||
|
let mut group_idents = Vec::new();
|
||||||
|
for group in &schema.groups {
|
||||||
|
let ident = format_ident!("CONFIG_GROUP_{}", group.id.to_shouty_snake_case());
|
||||||
|
let id = &group.id;
|
||||||
|
let name = &group.name;
|
||||||
|
let description = if let Some(description) = &group.description {
|
||||||
|
quote! { Some(#description) }
|
||||||
|
} else {
|
||||||
|
quote! { None }
|
||||||
|
};
|
||||||
|
let properties =
|
||||||
|
group.properties.iter().map(|p| format_ident!("{}", p.to_upper_camel_case()));
|
||||||
|
groups.extend(quote! {
|
||||||
|
ConfigPropertyGroup {
|
||||||
|
id: #id,
|
||||||
|
name: #name,
|
||||||
|
description: #description,
|
||||||
|
properties: &[#(ConfigPropertyId::#properties,)*],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
group_idents.push(ident);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut property_idents = Vec::new();
|
||||||
|
let mut property_variants = TokenStream::new();
|
||||||
|
let mut variant_info = TokenStream::new();
|
||||||
|
let mut config_property_id_to_str = TokenStream::new();
|
||||||
|
let mut config_property_id_to_name = TokenStream::new();
|
||||||
|
let mut config_property_id_to_description = TokenStream::new();
|
||||||
|
let mut config_property_id_to_kind = TokenStream::new();
|
||||||
|
let mut property_fields = TokenStream::new();
|
||||||
|
let mut default_fields = TokenStream::new();
|
||||||
|
let mut get_property_value_variants = TokenStream::new();
|
||||||
|
let mut set_property_value_variants = TokenStream::new();
|
||||||
|
let mut set_property_value_str_variants = TokenStream::new();
|
||||||
|
let mut config_property_id_from_str = TokenStream::new();
|
||||||
|
for property in &schema.properties {
|
||||||
|
let base = match property {
|
||||||
|
ConfigProperty::Boolean(b) => &b.base,
|
||||||
|
ConfigProperty::Choice(c) => &c.base,
|
||||||
|
};
|
||||||
|
let id = &base.id;
|
||||||
|
let enum_ident = format_ident!("{}", id.to_upper_camel_case());
|
||||||
|
property_idents.push(enum_ident.clone());
|
||||||
|
config_property_id_to_str.extend(quote! { Self::#enum_ident => #id, });
|
||||||
|
let name = &base.name;
|
||||||
|
config_property_id_to_name.extend(quote! { Self::#enum_ident => #name, });
|
||||||
|
if let Some(description) = &base.description {
|
||||||
|
config_property_id_to_description.extend(quote! {
|
||||||
|
Self::#enum_ident => Some(#description),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
config_property_id_to_description.extend(quote! {
|
||||||
|
Self::#enum_ident => None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let doc = build_doc(name, base.description.as_deref());
|
||||||
|
property_variants.extend(quote! { #doc #enum_ident, });
|
||||||
|
property_fields.extend(doc);
|
||||||
|
let field_ident = format_ident!("{}", id.to_snake_case());
|
||||||
|
match property {
|
||||||
|
ConfigProperty::Boolean(b) => {
|
||||||
|
let default = b.default;
|
||||||
|
if default {
|
||||||
|
property_fields.extend(quote! {
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
property_fields.extend(quote! {
|
||||||
|
pub #field_ident: bool,
|
||||||
|
});
|
||||||
|
default_fields.extend(quote! {
|
||||||
|
#field_ident: #default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ConfigProperty::Choice(_) => {
|
||||||
|
property_fields.extend(quote! {
|
||||||
|
pub #field_ident: #enum_ident,
|
||||||
|
});
|
||||||
|
default_fields.extend(quote! {
|
||||||
|
#field_ident: #enum_ident::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let property_value = match property {
|
||||||
|
ConfigProperty::Boolean(_) => {
|
||||||
|
quote! { ConfigPropertyValue::Boolean(self.#field_ident) }
|
||||||
|
}
|
||||||
|
ConfigProperty::Choice(_) => {
|
||||||
|
quote! { ConfigPropertyValue::Choice(self.#field_ident.as_str()) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
get_property_value_variants.extend(quote! {
|
||||||
|
ConfigPropertyId::#enum_ident => #property_value,
|
||||||
|
});
|
||||||
|
match property {
|
||||||
|
ConfigProperty::Boolean(_) => {
|
||||||
|
set_property_value_variants.extend(quote! {
|
||||||
|
ConfigPropertyId::#enum_ident => {
|
||||||
|
if let ConfigPropertyValue::Boolean(value) = value {
|
||||||
|
self.#field_ident = value;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
set_property_value_str_variants.extend(quote! {
|
||||||
|
ConfigPropertyId::#enum_ident => {
|
||||||
|
if let Ok(value) = value.parse() {
|
||||||
|
self.#field_ident = value;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ConfigProperty::Choice(_) => {
|
||||||
|
set_property_value_variants.extend(quote! {
|
||||||
|
ConfigPropertyId::#enum_ident => {
|
||||||
|
if let ConfigPropertyValue::Choice(value) = value {
|
||||||
|
if let Ok(value) = value.parse() {
|
||||||
|
self.#field_ident = value;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
set_property_value_str_variants.extend(quote! {
|
||||||
|
ConfigPropertyId::#enum_ident => {
|
||||||
|
if let Ok(value) = value.parse() {
|
||||||
|
self.#field_ident = value;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let description = if let Some(description) = &base.description {
|
||||||
|
quote! { Some(#description) }
|
||||||
|
} else {
|
||||||
|
quote! { None }
|
||||||
|
};
|
||||||
|
variant_info.extend(quote! {
|
||||||
|
ConfigEnumVariantInfo {
|
||||||
|
value: #id,
|
||||||
|
name: #name,
|
||||||
|
description: #description,
|
||||||
|
is_default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
match property {
|
||||||
|
ConfigProperty::Boolean(_) => {
|
||||||
|
config_property_id_to_kind.extend(quote! {
|
||||||
|
Self::#enum_ident => ConfigPropertyKind::Boolean,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ConfigProperty::Choice(_) => {
|
||||||
|
config_property_id_to_kind.extend(quote! {
|
||||||
|
Self::#enum_ident => ConfigPropertyKind::Choice(#enum_ident::variant_info()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let snake_id = id.to_snake_case();
|
||||||
|
config_property_id_from_str.extend(quote! {
|
||||||
|
if s.eq_ignore_ascii_case(#id) || s.eq_ignore_ascii_case(#snake_id) {
|
||||||
|
return Ok(Self::#enum_ident);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = quote! {
|
||||||
|
pub trait ConfigEnum: Sized {
|
||||||
|
fn variants() -> &'static [Self];
|
||||||
|
fn variant_info() -> &'static [ConfigEnumVariantInfo];
|
||||||
|
fn as_str(&self) -> &'static str;
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
fn description(&self) -> Option<&'static str>;
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ConfigEnumVariantInfo {
|
||||||
|
pub value: &'static str,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub description: Option<&'static str>,
|
||||||
|
pub is_default: bool,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub enum ConfigPropertyId {
|
||||||
|
#property_variants
|
||||||
|
}
|
||||||
|
impl ConfigEnum for ConfigPropertyId {
|
||||||
|
#[inline]
|
||||||
|
fn variants() -> &'static [Self] {
|
||||||
|
static VARIANTS: &[ConfigPropertyId] = &[#(ConfigPropertyId::#property_idents,)*];
|
||||||
|
VARIANTS
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn variant_info() -> &'static [ConfigEnumVariantInfo] {
|
||||||
|
static VARIANT_INFO: &[ConfigEnumVariantInfo] = &[
|
||||||
|
#variant_info
|
||||||
|
];
|
||||||
|
VARIANT_INFO
|
||||||
|
}
|
||||||
|
fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#config_property_id_to_str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#config_property_id_to_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn description(&self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
#config_property_id_to_description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ConfigPropertyId {
|
||||||
|
pub fn kind(&self) -> ConfigPropertyKind {
|
||||||
|
match self {
|
||||||
|
#config_property_id_to_kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::str::FromStr for ConfigPropertyId {
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
#config_property_id_from_str
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ConfigPropertyGroup {
|
||||||
|
pub id: &'static str,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub description: Option<&'static str>,
|
||||||
|
pub properties: &'static [ConfigPropertyId],
|
||||||
|
}
|
||||||
|
pub static CONFIG_GROUPS: &[ConfigPropertyGroup] = &[#groups];
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub enum ConfigPropertyValue {
|
||||||
|
Boolean(bool),
|
||||||
|
Choice(&'static str),
|
||||||
|
}
|
||||||
|
impl ConfigPropertyValue {
|
||||||
|
pub fn to_json(&self) -> serde_json::Value {
|
||||||
|
match self {
|
||||||
|
ConfigPropertyValue::Boolean(value) => serde_json::Value::Bool(*value),
|
||||||
|
ConfigPropertyValue::Choice(value) => serde_json::Value::String(value.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ConfigPropertyKind {
|
||||||
|
Boolean,
|
||||||
|
Choice(&'static [ConfigEnumVariantInfo]),
|
||||||
|
}
|
||||||
|
#enums
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_true() -> bool { true }
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify), tsify(from_wasm_abi))]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct DiffObjConfig {
|
||||||
|
#property_fields
|
||||||
|
}
|
||||||
|
impl Default for DiffObjConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
#default_fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DiffObjConfig {
|
||||||
|
pub fn get_property_value(&self, id: ConfigPropertyId) -> ConfigPropertyValue {
|
||||||
|
match id {
|
||||||
|
#get_property_value_variants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
|
pub fn set_property_value(&mut self, id: ConfigPropertyId, value: ConfigPropertyValue) -> Result<(), ()> {
|
||||||
|
match id {
|
||||||
|
#set_property_value_variants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
|
pub fn set_property_value_str(&mut self, id: ConfigPropertyId, value: &str) -> Result<(), ()> {
|
||||||
|
match id {
|
||||||
|
#set_property_value_str_variants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let file = syn::parse2(tokens).unwrap();
|
||||||
|
let formatted = prettyplease::unparse(&file);
|
||||||
|
std::fs::write(
|
||||||
|
PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("config.gen.rs"),
|
||||||
|
formatted,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
@@ -21,9 +21,9 @@ enum SymbolFlag {
|
|||||||
SYMBOL_NONE = 0;
|
SYMBOL_NONE = 0;
|
||||||
SYMBOL_GLOBAL = 1;
|
SYMBOL_GLOBAL = 1;
|
||||||
SYMBOL_LOCAL = 2;
|
SYMBOL_LOCAL = 2;
|
||||||
SYMBOL_WEAK = 3;
|
SYMBOL_WEAK = 4;
|
||||||
SYMBOL_COMMON = 4;
|
SYMBOL_COMMON = 8;
|
||||||
SYMBOL_HIDDEN = 5;
|
SYMBOL_HIDDEN = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A single parsed instruction
|
// A single parsed instruction
|
||||||
@@ -122,10 +122,17 @@ message InstructionBranchTo {
|
|||||||
uint32 branch_index = 2;
|
uint32 branch_index = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FunctionDiff {
|
message SymbolRef {
|
||||||
|
optional uint32 section_index = 1;
|
||||||
|
uint32 symbol_index = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SymbolDiff {
|
||||||
Symbol symbol = 1;
|
Symbol symbol = 1;
|
||||||
repeated InstructionDiff instructions = 2;
|
repeated InstructionDiff instructions = 2;
|
||||||
optional float match_percent = 3;
|
optional float match_percent = 3;
|
||||||
|
// The symbol ref in the _other_ object that this symbol was diffed against
|
||||||
|
optional SymbolRef target = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DataDiff {
|
message DataDiff {
|
||||||
@@ -140,7 +147,7 @@ message SectionDiff {
|
|||||||
SectionKind kind = 2;
|
SectionKind kind = 2;
|
||||||
uint64 size = 3;
|
uint64 size = 3;
|
||||||
uint64 address = 4;
|
uint64 address = 4;
|
||||||
repeated FunctionDiff functions = 5;
|
repeated SymbolDiff symbols = 5;
|
||||||
repeated DataDiff data = 6;
|
repeated DataDiff data = 6;
|
||||||
optional float match_percent = 7;
|
optional float match_percent = 7;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user