mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-11 06:27:55 +00:00
Support overriding diff options in project config (& for individual units) (#263)
* Support loading diff options from project config * Support per-unit option overrides
This commit is contained in:
@@ -24,7 +24,8 @@ use objdiff_core::{
|
||||
watcher::{Watcher, create_watcher},
|
||||
},
|
||||
config::{
|
||||
ProjectConfig, ProjectObject, ProjectObjectMetadata, build_globset,
|
||||
ProjectConfig, ProjectObject, ProjectObjectMetadata, ProjectOptions, apply_project_options,
|
||||
build_globset,
|
||||
path::{check_path_buf, platform_path, platform_path_serde_option},
|
||||
},
|
||||
diff::{DiffObjConfig, MappingConfig, ObjectDiff},
|
||||
@@ -77,11 +78,11 @@ pub struct Args {
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let (target_path, base_path, project_config) =
|
||||
let (target_path, base_path, project_config, unit_options) =
|
||||
match (&args.target, &args.base, &args.project, &args.unit) {
|
||||
(Some(_), Some(_), None, None)
|
||||
| (Some(_), None, None, None)
|
||||
| (None, Some(_), None, None) => (args.target.clone(), args.base.clone(), None),
|
||||
| (None, Some(_), None, None) => (args.target.clone(), args.base.clone(), None, None),
|
||||
(None, None, p, u) => {
|
||||
let project = match p {
|
||||
Some(project) => project.clone(),
|
||||
@@ -106,28 +107,32 @@ pub fn run(args: Args) -> Result<()> {
|
||||
.base_dir
|
||||
.as_ref()
|
||||
.map(|p| project.join(p.with_platform_encoding()));
|
||||
let objects = project_config
|
||||
.units
|
||||
let units = project_config.units.as_deref().unwrap_or_default();
|
||||
let objects = units
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|o| {
|
||||
ObjectConfig::new(
|
||||
o,
|
||||
&project,
|
||||
target_obj_dir.as_deref(),
|
||||
base_obj_dir.as_deref(),
|
||||
.enumerate()
|
||||
.map(|(idx, o)| {
|
||||
(
|
||||
ObjectConfig::new(
|
||||
o,
|
||||
&project,
|
||||
target_obj_dir.as_deref(),
|
||||
base_obj_dir.as_deref(),
|
||||
),
|
||||
idx,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let object = if let Some(u) = u {
|
||||
let (object, unit_idx) = if let Some(u) = u {
|
||||
objects
|
||||
.iter()
|
||||
.find(|obj| obj.name == *u)
|
||||
.find(|(obj, _)| obj.name == *u)
|
||||
.map(|(obj, idx)| (obj, *idx))
|
||||
.ok_or_else(|| anyhow!("Unit not found: {}", u))?
|
||||
} else if let Some(symbol_name) = &args.symbol {
|
||||
let mut idx = None;
|
||||
let mut count = 0usize;
|
||||
for (i, obj) in objects.iter().enumerate() {
|
||||
for (i, (obj, unit_idx)) in objects.iter().enumerate() {
|
||||
if obj
|
||||
.target_path
|
||||
.as_deref()
|
||||
@@ -135,7 +140,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
.transpose()?
|
||||
.unwrap_or(false)
|
||||
{
|
||||
idx = Some(i);
|
||||
idx = Some((i, *unit_idx));
|
||||
count += 1;
|
||||
if count > 1 {
|
||||
break;
|
||||
@@ -144,7 +149,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
match (count, idx) {
|
||||
(0, None) => bail!("Symbol not found: {}", symbol_name),
|
||||
(1, Some(i)) => &objects[i],
|
||||
(1, Some((i, unit_idx))) => (&objects[i].0, unit_idx),
|
||||
(2.., Some(_)) => bail!(
|
||||
"Multiple instances of {} were found, try specifying a unit",
|
||||
symbol_name
|
||||
@@ -154,18 +159,29 @@ pub fn run(args: Args) -> Result<()> {
|
||||
} else {
|
||||
bail!("Must specify one of: symbol, project and unit, target and base objects")
|
||||
};
|
||||
let unit_options = units.get(unit_idx).and_then(|u| u.options().cloned());
|
||||
let target_path = object.target_path.clone();
|
||||
let base_path = object.base_path.clone();
|
||||
(target_path, base_path, Some(project_config))
|
||||
(target_path, base_path, Some(project_config), unit_options)
|
||||
}
|
||||
_ => bail!("Either target and base or project and unit must be specified"),
|
||||
};
|
||||
|
||||
run_interactive(args, target_path, base_path, project_config)
|
||||
run_interactive(args, target_path, base_path, project_config, unit_options)
|
||||
}
|
||||
|
||||
fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)> {
|
||||
fn build_config_from_args(
|
||||
args: &Args,
|
||||
project_config: Option<&ProjectConfig>,
|
||||
unit_options: Option<&ProjectOptions>,
|
||||
) -> Result<(DiffObjConfig, MappingConfig)> {
|
||||
let mut diff_config = DiffObjConfig::default();
|
||||
if let Some(options) = project_config.and_then(|config| config.options.as_ref()) {
|
||||
apply_project_options(&mut diff_config, options)?;
|
||||
}
|
||||
if let Some(options) = unit_options {
|
||||
apply_project_options(&mut diff_config, options)?;
|
||||
}
|
||||
apply_config_args(&mut diff_config, &args.config)?;
|
||||
let mut mapping_config = MappingConfig {
|
||||
mappings: Default::default(),
|
||||
@@ -316,11 +332,13 @@ fn run_interactive(
|
||||
target_path: Option<Utf8PlatformPathBuf>,
|
||||
base_path: Option<Utf8PlatformPathBuf>,
|
||||
project_config: Option<ProjectConfig>,
|
||||
unit_options: Option<ProjectOptions>,
|
||||
) -> Result<()> {
|
||||
let Some(symbol_name) = &args.symbol else { bail!("Interactive mode requires a symbol name") };
|
||||
let time_format = time::format_description::parse_borrowed::<2>("[hour]:[minute]:[second]")
|
||||
.context("Failed to parse time format")?;
|
||||
let (diff_obj_config, mapping_config) = build_config_from_args(&args)?;
|
||||
let (diff_obj_config, mapping_config) =
|
||||
build_config_from_args(&args, project_config.as_ref(), unit_options.as_ref())?;
|
||||
let mut state = AppState {
|
||||
jobs: Default::default(),
|
||||
waker: Default::default(),
|
||||
|
||||
@@ -7,7 +7,7 @@ use objdiff_core::{
|
||||
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, REPORT_VERSION,
|
||||
Report, ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
|
||||
},
|
||||
config::path::platform_path,
|
||||
config::{ProjectObject, ProjectOptions, apply_project_options, path::platform_path},
|
||||
diff,
|
||||
obj::{self, SectionKind, SymbolFlag, SymbolKind},
|
||||
};
|
||||
@@ -83,14 +83,13 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
|
||||
fn generate(args: GenerateArgs) -> Result<()> {
|
||||
let mut diff_config = diff::DiffObjConfig {
|
||||
let base_diff_config = diff::DiffObjConfig {
|
||||
function_reloc_diffs: diff::FunctionRelocDiffs::None,
|
||||
combine_data_sections: true,
|
||||
combine_text_sections: true,
|
||||
ppc_calculate_pool_relocations: false,
|
||||
..Default::default()
|
||||
};
|
||||
apply_config_args(&mut diff_config, &args.config)?;
|
||||
|
||||
let output_format = OutputFormat::from_option(args.format.as_deref())?;
|
||||
let project_dir = args.project.as_deref().unwrap_or_else(|| Utf8PlatformPath::new("."));
|
||||
@@ -101,31 +100,44 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
||||
Some((Err(err), _)) => bail!("Failed to load project configuration: {}", err),
|
||||
None => bail!("No project configuration found"),
|
||||
};
|
||||
info!(
|
||||
"Generating report for {} units (using {} threads)",
|
||||
project.units().len(),
|
||||
if args.deduplicate { 1 } else { rayon::current_num_threads() }
|
||||
);
|
||||
|
||||
let target_obj_dir =
|
||||
project.target_dir.as_ref().map(|p| project_dir.join(p.with_platform_encoding()));
|
||||
let base_obj_dir =
|
||||
project.base_dir.as_ref().map(|p| project_dir.join(p.with_platform_encoding()));
|
||||
let objects = project
|
||||
.units
|
||||
let project_units = project.units.as_deref().unwrap_or_default();
|
||||
let objects = project_units
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|o| {
|
||||
ObjectConfig::new(o, project_dir, target_obj_dir.as_deref(), base_obj_dir.as_deref())
|
||||
.enumerate()
|
||||
.map(|(idx, o)| {
|
||||
(
|
||||
ObjectConfig::new(
|
||||
o,
|
||||
project_dir,
|
||||
target_obj_dir.as_deref(),
|
||||
base_obj_dir.as_deref(),
|
||||
),
|
||||
idx,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
info!(
|
||||
"Generating report for {} units (using {} threads)",
|
||||
objects.len(),
|
||||
if args.deduplicate { 1 } else { rayon::current_num_threads() }
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
let mut units = vec![];
|
||||
let mut existing_functions: HashSet<String> = HashSet::new();
|
||||
if args.deduplicate {
|
||||
// If deduplicating, we need to run single-threaded
|
||||
for object in &objects {
|
||||
for (object, unit_idx) in &objects {
|
||||
let diff_config = build_unit_diff_config(
|
||||
&base_diff_config,
|
||||
project.options.as_ref(),
|
||||
project_units.get(*unit_idx).and_then(ProjectObject::options),
|
||||
&args.config,
|
||||
)?;
|
||||
if let Some(unit) = report_object(object, &diff_config, Some(&mut existing_functions))?
|
||||
{
|
||||
units.push(unit);
|
||||
@@ -134,7 +146,15 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
||||
} else {
|
||||
let vec = objects
|
||||
.par_iter()
|
||||
.map(|object| report_object(object, &diff_config, None))
|
||||
.map(|(object, unit_idx)| {
|
||||
let diff_config = build_unit_diff_config(
|
||||
&base_diff_config,
|
||||
project.options.as_ref(),
|
||||
project_units.get(*unit_idx).and_then(ProjectObject::options),
|
||||
&args.config,
|
||||
)?;
|
||||
report_object(object, &diff_config, None)
|
||||
})
|
||||
.collect::<Result<Vec<Option<ReportUnit>>>>()?;
|
||||
units = vec.into_iter().flatten().collect();
|
||||
}
|
||||
@@ -156,6 +176,24 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_unit_diff_config(
|
||||
base: &diff::DiffObjConfig,
|
||||
project_options: Option<&ProjectOptions>,
|
||||
unit_options: Option<&ProjectOptions>,
|
||||
cli_args: &[String],
|
||||
) -> Result<diff::DiffObjConfig> {
|
||||
let mut diff_config = base.clone();
|
||||
if let Some(options) = project_options {
|
||||
apply_project_options(&mut diff_config, options)?;
|
||||
}
|
||||
if let Some(options) = unit_options {
|
||||
apply_project_options(&mut diff_config, options)?;
|
||||
}
|
||||
// CLI args override project and unit options
|
||||
apply_config_args(&mut diff_config, cli_args)?;
|
||||
Ok(diff_config)
|
||||
}
|
||||
|
||||
fn report_object(
|
||||
object: &ObjectConfig,
|
||||
diff_config: &diff::DiffObjConfig,
|
||||
|
||||
Reference in New Issue
Block a user