From f70c3b3d2e029e631e004939887cb921c7db1688 Mon Sep 17 00:00:00 2001 From: Nikita Romanov Date: Sat, 14 Feb 2026 20:38:46 +0200 Subject: [PATCH 1/2] Support different format stratagies for compiler executable path --- bear/src/config.rs | 2 ++ bear/src/output/clang/converter.rs | 22 ++++++++++++++++------ bear/src/output/clang/format.rs | 7 +++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/bear/src/config.rs b/bear/src/config.rs index 4094a4b9..0a294b47 100644 --- a/bear/src/config.rs +++ b/bear/src/config.rs @@ -275,6 +275,8 @@ mod types { pub directory: PathResolver, #[serde(default)] pub file: PathResolver, + #[serde(default)] + pub executable: PathResolver, } /// Path resolver options matching the YAML format. diff --git a/bear/src/output/clang/converter.rs b/bear/src/output/clang/converter.rs index 47ee1786..95391c45 100644 --- a/bear/src/output/clang/converter.rs +++ b/bear/src/output/clang/converter.rs @@ -186,6 +186,19 @@ impl CommandConverter { } } + /// Formats an executable file path + /// + /// Returns the formatted path, falling back to the original path on error. + fn format_executable(&self, formatted_directory: &Path, executable: &Path) -> PathBuf { + match self.path_formatter.format_executable(formatted_directory, executable) { + Ok(formatted_path) => formatted_path, + Err(e) => { + warn!("Failed to format executable path {}: {}", executable.display(), e); + executable.to_path_buf() + } + } + } + /// Builds command arguments for a specific source file. /// /// This method constructs the command arguments list that includes the executable, @@ -242,12 +255,9 @@ impl CommandConverter { command_args.extend(formatted_args); } ArgumentKind::Compiler => { - if let Some(executable_name) = cmd.executable.file_name() { - if let Some(name_str) = executable_name.to_str() { - command_args.push(name_str.to_string()); - } else { - command_args.extend(original_args); - } + let executable = self.format_executable(&formatted_directory, &cmd.executable); + if let Some(executable_str) = executable.to_str() { + command_args.push(executable_str.to_string()); } else { command_args.extend(original_args); } diff --git a/bear/src/output/clang/format.rs b/bear/src/output/clang/format.rs index adc954c9..27872596 100644 --- a/bear/src/output/clang/format.rs +++ b/bear/src/output/clang/format.rs @@ -37,6 +37,9 @@ pub trait PathFormatter: Send + Sync { /// Format a file path according to the configured strategy. fn format_file(&self, directory: &Path, file: &Path) -> Result; + + /// Format an executable path according to the configured strategy. + fn format_executable(&self, directory: &Path, file: &Path) -> Result; } /// Implementation of PathFormatter that uses the configuration to determine @@ -62,6 +65,10 @@ impl PathFormatter for ConfigurablePathFormatter { fn format_file(&self, directory: &Path, file: &Path) -> Result { self.config.file.resolve(directory, file) } + + fn format_executable(&self, directory: &Path, executable: &Path) -> Result { + self.config.executable.resolve(directory, executable) + } } impl PathResolver { From 87c8f8acf933d220ac886f26b9661c98a0fb55d6 Mon Sep 17 00:00:00 2001 From: Nikita Romanov Date: Sat, 14 Feb 2026 21:34:53 +0200 Subject: [PATCH 2/2] check that config executable format follows same rules file --- bear/src/config.rs | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/bear/src/config.rs b/bear/src/config.rs index 0a294b47..8b5fac3b 100644 --- a/bear/src/config.rs +++ b/bear/src/config.rs @@ -369,7 +369,7 @@ pub mod validation { #[error("Duplicate {field} entry at: {idx}")] DuplicateEntry { field: &'static str, idx: usize }, #[error("Path format error: {message}")] - PathFormatError { message: &'static str }, + PathFormatError { message: String }, #[error("Multiple validation errors: {errors:?}")] Multiple { errors: Vec }, } @@ -521,6 +521,29 @@ pub mod validation { } } + fn validate_path_dependency( + base: &PathResolver, + dependent: &PathResolver, + dependent_name: &str, + ) -> Result<(), ValidationError> { + use PathResolver::*; + + let conflict = match (base, dependent) { + (Relative, Absolute | Canonical) => Some("must be relative too"), + (Canonical, Absolute) => Some("can't be absolute"), + (Absolute, Canonical) => Some("can't be canonical"), + (AsIs, Absolute | Relative | Canonical) => Some("must also be AsIs"), + _ => None, + }; + + match conflict { + Some(reason) => Err(ValidationError::PathFormatError { + message: format!("When directory is {base:?}, {dependent_name} {reason}"), + }), + None => Ok(()), + } + } + impl Validator for PathFormat { type Error = ValidationError; @@ -529,23 +552,9 @@ pub mod validation { /// - When directory is canonical, file can't be absolute /// - When directory is absolute, file can't be canonical fn validate(config: &PathFormat) -> Result<(), Self::Error> { - use PathResolver::*; - - match (&config.directory, &config.file) { - (Relative, Absolute | Canonical) => Err(ValidationError::PathFormatError { - message: "When directory is relative, file must be relative too", - }), - (Canonical, Absolute) => Err(ValidationError::PathFormatError { - message: "When directory is canonical, file can't be absolute", - }), - (Absolute, Canonical) => Err(ValidationError::PathFormatError { - message: "When directory is absolute, file can't be canonical", - }), - (AsIs, Absolute | Relative | Canonical) => Err(ValidationError::PathFormatError { - message: "When directory as-is, file should be the same", - }), - _ => Ok(()), - } + validate_path_dependency(&config.directory, &config.file, "file")?; + validate_path_dependency(&config.directory, &config.executable, "executable")?; + Ok(()) } }