rust: config for source file filtering reviewed

This commit is contained in:
Laszlo Nagy
2025-01-07 22:39:10 +11:00
parent b69ac1d6ed
commit eee1dd1fcd
6 changed files with 317 additions and 257 deletions

View File

@@ -29,42 +29,46 @@
//! executables:
//! - /usr/bin/cc
//! - /usr/bin/c++
//! - /usr/bin/clang
//! - /usr/bin/clang++
//! output:
//! specification: clang
//! compilers:
//! - path: /usr/local/bin/cc
//! ignore: always
//! - path: /usr/local/bin/c++
//! - path: /usr/bin/cc
//! ignore: never
//! - path: /usr/bin/c++
//! ignore: conditional
//! arguments:
//! match:
//! - -###
//! - path: /usr/local/bin/clang
//! - path: /usr/bin/clang
//! ignore: never
//! arguments:
//! add:
//! - -DDEBUG
//! remove:
//! - -Wall
//! - path: /usr/local/bin/clang++
//! - path: /usr/bin/clang++
//! arguments:
//! remove:
//! - -Wall
//! filter:
//! source:
//! include_only_existing_files: true
//! paths_to_include:
//! - sources
//! paths_to_exclude:
//! - tests
//! duplicates:
//! by_fields:
//! - file
//! - directory
//! sources:
//! only_existing_files: true
//! paths:
//! - path: /opt/project/sources
//! ignore: never
//! - path: /opt/project/tests
//! ignore: always
//! duplicates:
//! by_fields:
//! - file
//! - directory
//! format:
//! command_as_array: true
//! drop_output_field: false
//! use_absolute_path: false
//! paths_as: canonical
//! ```
//!
//! ```yaml
@@ -298,7 +302,9 @@ pub enum Output {
#[serde(default)]
compilers: Vec<Compiler>,
#[serde(default)]
filter: Filter,
sources: SourceFilter,
#[serde(default)]
duplicates: DuplicateFilter,
#[serde(default)]
format: Format,
},
@@ -311,7 +317,8 @@ impl Default for Output {
fn default() -> Self {
Output::Clang {
compilers: vec![],
filter: Filter::default(),
sources: SourceFilter::default(),
duplicates: DuplicateFilter::default(),
format: Format::default(),
}
}
@@ -323,14 +330,17 @@ impl Validate for Output {
match self {
Output::Clang {
compilers,
filter,
sources,
duplicates,
format,
} => {
let compilers = compilers.validate()?;
let filter = filter.validate()?;
let sources = sources.validate()?;
let duplicates = duplicates.validate()?;
Ok(Output::Clang {
compilers,
filter,
sources,
duplicates,
format,
})
}
@@ -346,8 +356,8 @@ impl Validate for Output {
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Compiler {
pub path: PathBuf,
#[serde(default = "default_never_ignore")]
pub ignore: Ignore,
#[serde(default)]
pub ignore: IgnoreOrConsider,
#[serde(default)]
pub arguments: Arguments,
}
@@ -371,19 +381,19 @@ impl Validate for Compiler {
/// Validate the configuration of the compiler.
fn validate(self) -> Result<Self> {
match self.ignore {
Ignore::Always if self.arguments != Arguments::default() => {
IgnoreOrConsider::Always if self.arguments != Arguments::default() => {
anyhow::bail!(
"All arguments must be empty in always ignore mode. {:?}",
self.path
);
}
Ignore::Conditional if self.arguments.match_.is_empty() => {
IgnoreOrConsider::Conditional if self.arguments.match_.is_empty() => {
anyhow::bail!(
"The match arguments cannot be empty in conditional ignore mode. {:?}",
self.path
);
}
Ignore::Never if !self.arguments.match_.is_empty() => {
IgnoreOrConsider::Never if !self.arguments.match_.is_empty() => {
anyhow::bail!(
"The arguments must be empty in never ignore mode. {:?}",
self.path
@@ -401,16 +411,23 @@ impl Validate for Compiler {
///
/// The meaning of the possible values are:
/// - Always: Always ignore the compiler call.
/// - Conditional: Ignore the compiler call if the arguments match.
/// - Never: Never ignore the compiler call. (Default)
/// - Conditional: Ignore the compiler call if the arguments match.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Ignore {
#[serde(rename = "always")]
pub enum IgnoreOrConsider {
#[serde(rename = "always", alias = "true")]
Always,
#[serde(rename = "never", alias = "false")]
Never,
#[serde(rename = "conditional")]
Conditional,
#[serde(rename = "never")]
Never,
}
/// The default ignore mode is never ignore.
impl Default for IgnoreOrConsider {
fn default() -> Self {
IgnoreOrConsider::Never
}
}
/// Argument lists to match, add or remove.
@@ -430,46 +447,49 @@ pub struct Arguments {
pub remove: Vec<String>,
}
/// Filter configuration is used to filter the compiler calls.
///
/// Allow to filter the compiler calls by compiler, source files and duplicates.
///
/// - Compilers: Specify on the compiler path and arguments.
/// - Source: Specify the source file location.
/// - Duplicates: Specify the fields of the JSON compilation database record to detect duplicates.
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct Filter {
#[serde(default)]
pub source: SourceFilter,
#[serde(default)]
pub duplicates: DuplicateFilter,
}
impl Validate for Filter {
/// Validate the configuration of the output writer.
fn validate(self) -> Result<Self> {
self.duplicates.validate().map(|duplicates| Filter {
source: self.source,
duplicates,
})
}
}
/// Source filter configuration is used to filter the compiler calls based on the source files.
///
/// Allow to filter the compiler calls based on the source files.
///
/// - Include only existing files: can be true or false.
/// - Paths to include: Only include the compiler calls that compiles source files from this path.
/// - Paths to exclude: Exclude the compiler calls that compiles source files from this path.
/// - List of directories to include or exclude.
/// (The order of these entries will imply the order of evaluation.)
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct SourceFilter {
#[serde(default = "default_disabled")]
pub include_only_existing_files: bool,
pub only_existing_files: bool,
#[serde(default)]
pub paths_to_include: Vec<PathBuf>,
#[serde(default)]
pub paths_to_exclude: Vec<PathBuf>,
pub paths: Vec<DirectoryFilter>,
}
impl Validate for SourceFilter {
/// Fail when the same directory is in multiple times in the list.
/// Otherwise, return the received source filter.
fn validate(self) -> Result<Self> {
let mut already_seen = HashSet::new();
for directory in &self.paths {
if !already_seen.insert(&directory.path) {
anyhow::bail!("The directory {:?} is duplicated.", directory.path);
}
}
Ok(self)
}
}
/// Directory filter configuration is used to filter the compiler calls based on
/// the source file location.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DirectoryFilter {
pub path: PathBuf,
pub ignore: Ignore,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Ignore {
#[serde(rename = "always", alias = "true")]
Always,
#[serde(rename = "never", alias = "false")]
Never,
}
/// Duplicate filter configuration is used to filter the duplicate compiler calls.
@@ -524,8 +544,8 @@ pub struct Format {
pub command_as_array: bool,
#[serde(default = "default_disabled")]
pub drop_output_field: bool,
#[serde(default = "default_disabled")]
pub use_absolute_path: bool,
#[serde(default)]
pub paths_as: PathFormat,
}
impl Default for Format {
@@ -533,11 +553,29 @@ impl Default for Format {
Format {
command_as_array: true,
drop_output_field: false,
use_absolute_path: false,
paths_as: PathFormat::default(),
}
}
}
/// Path format configuration describes how the paths should be formatted.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum PathFormat {
#[serde(rename = "original", alias = "is")]
Original,
#[serde(rename = "absolute")]
Absolute,
#[serde(rename = "canonical")]
Canonical,
}
/// The default path format is the original path.
impl Default for PathFormat {
fn default() -> Self {
PathFormat::Original
}
}
fn default_disabled() -> bool {
false
}
@@ -561,11 +599,6 @@ fn default_preload_library() -> PathBuf {
PathBuf::from(PRELOAD_LIBRARY_PATH)
}
/// The default don't ignore the compiler.
fn default_never_ignore() -> Ignore {
Ignore::Never
}
// Custom deserialization function to validate the schema version
fn validate_schema_version<'de, D>(deserializer: D) -> std::result::Result<String, D::Error>
where
@@ -610,42 +643,46 @@ mod test {
executables:
- /usr/bin/cc
- /usr/bin/c++
- /usr/bin/clang
- /usr/bin/clang++
output:
specification: clang
compilers:
- path: /usr/local/bin/cc
ignore: always
- path: /usr/local/bin/c++
- path: /usr/bin/cc
ignore: never
- path: /usr/bin/c++
ignore: conditional
arguments:
match:
- -###
- path: /usr/local/bin/clang
- path: /usr/bin/clang
ignore: never
arguments:
add:
- -DDEBUG
remove:
- -Wall
- path: /usr/local/bin/clang++
- path: /usr/bin/clang++
arguments:
remove:
- -Wall
filter:
source:
include_only_existing_files: true
paths_to_include:
- sources
paths_to_exclude:
- tests
duplicates:
by_fields:
- file
- directory
sources:
only_existing_files: true
paths:
- path: /opt/project/sources
ignore: never
- path: /opt/project/tests
ignore: always
duplicates:
by_fields:
- file
- directory
format:
command_as_array: true
drop_output_field: false
use_absolute_path: true
paths_as: canonical
"#;
let result = Main::from_reader(content).unwrap();
@@ -654,26 +691,36 @@ mod test {
intercept: Intercept::Wrapper {
path: default_wrapper_executable(),
directory: PathBuf::from("/tmp"),
executables: vec_of_pathbuf!["/usr/bin/cc", "/usr/bin/c++"],
executables: vec_of_pathbuf![
"/usr/bin/cc",
"/usr/bin/c++",
"/usr/bin/clang",
"/usr/bin/clang++"
],
},
output: Output::Clang {
compilers: vec![
Compiler {
path: PathBuf::from("/usr/local/bin/cc"),
ignore: Ignore::Always,
ignore: IgnoreOrConsider::Always,
arguments: Arguments::default(),
},
Compiler {
path: PathBuf::from("/usr/local/bin/c++"),
ignore: Ignore::Conditional,
path: PathBuf::from("/usr/bin/cc"),
ignore: IgnoreOrConsider::Never,
arguments: Arguments::default(),
},
Compiler {
path: PathBuf::from("/usr/bin/c++"),
ignore: IgnoreOrConsider::Conditional,
arguments: Arguments {
match_: vec_of_strings!["-###"],
..Default::default()
},
},
Compiler {
path: PathBuf::from("/usr/local/bin/clang"),
ignore: Ignore::Never,
path: PathBuf::from("/usr/bin/clang"),
ignore: IgnoreOrConsider::Never,
arguments: Arguments {
add: vec_of_strings!["-DDEBUG"],
remove: vec_of_strings!["-Wall"],
@@ -681,28 +728,34 @@ mod test {
},
},
Compiler {
path: PathBuf::from("/usr/local/bin/clang++"),
ignore: Ignore::Never,
path: PathBuf::from("/usr/bin/clang++"),
ignore: IgnoreOrConsider::Never,
arguments: Arguments {
remove: vec_of_strings!["-Wall"],
..Default::default()
},
},
],
filter: Filter {
source: SourceFilter {
include_only_existing_files: true,
paths_to_include: vec_of_pathbuf!["sources"],
paths_to_exclude: vec_of_pathbuf!["tests"],
},
duplicates: DuplicateFilter {
by_fields: vec![OutputFields::File, OutputFields::Directory],
},
sources: SourceFilter {
only_existing_files: true,
paths: vec![
DirectoryFilter {
path: PathBuf::from("/opt/project/sources"),
ignore: Ignore::Never,
},
DirectoryFilter {
path: PathBuf::from("/opt/project/tests"),
ignore: Ignore::Always,
},
],
},
duplicates: DuplicateFilter {
by_fields: vec![OutputFields::File, OutputFields::Directory],
},
format: Format {
command_as_array: true,
drop_output_field: false,
use_absolute_path: true,
paths_as: PathFormat::Canonical,
},
},
schema: String::from("4.0"),
@@ -723,13 +776,12 @@ mod test {
- /usr/bin/c++
output:
specification: clang
filter:
source:
include_only_existing_files: true
duplicates:
by_fields:
- file
- directory
sources:
only_existing_files: true
duplicates:
by_fields:
- file
- directory
format:
command_as_array: true
"#;
@@ -744,20 +796,17 @@ mod test {
},
output: Output::Clang {
compilers: vec![],
filter: Filter {
source: SourceFilter {
include_only_existing_files: true,
paths_to_include: vec_of_pathbuf![],
paths_to_exclude: vec_of_pathbuf![],
},
duplicates: DuplicateFilter {
by_fields: vec![OutputFields::File, OutputFields::Directory],
},
sources: SourceFilter {
only_existing_files: true,
paths: vec![],
},
duplicates: DuplicateFilter {
by_fields: vec![OutputFields::File, OutputFields::Directory],
},
format: Format {
command_as_array: true,
drop_output_field: false,
use_absolute_path: false,
paths_as: PathFormat::Original,
},
},
schema: String::from("4.0"),
@@ -807,12 +856,11 @@ mod test {
ignore: always
- path: /usr/local/bin/clang++
ignore: always
filter:
source:
include_only_existing_files: false
duplicates:
by_fields:
- file
sources:
only_existing_files: false
duplicates:
by_fields:
- file
format:
command_as_array: true
drop_output_field: true
@@ -829,39 +877,36 @@ mod test {
compilers: vec![
Compiler {
path: PathBuf::from("/usr/local/bin/cc"),
ignore: Ignore::Never,
ignore: IgnoreOrConsider::Never,
arguments: Arguments::default(),
},
Compiler {
path: PathBuf::from("/usr/local/bin/c++"),
ignore: Ignore::Never,
ignore: IgnoreOrConsider::Never,
arguments: Arguments::default(),
},
Compiler {
path: PathBuf::from("/usr/local/bin/clang"),
ignore: Ignore::Always,
ignore: IgnoreOrConsider::Always,
arguments: Arguments::default(),
},
Compiler {
path: PathBuf::from("/usr/local/bin/clang++"),
ignore: Ignore::Always,
ignore: IgnoreOrConsider::Always,
arguments: Arguments::default(),
},
],
filter: Filter {
source: SourceFilter {
include_only_existing_files: false,
paths_to_include: vec_of_pathbuf![],
paths_to_exclude: vec_of_pathbuf![],
},
duplicates: DuplicateFilter {
by_fields: vec![OutputFields::File],
},
sources: SourceFilter {
only_existing_files: false,
paths: vec![],
},
duplicates: DuplicateFilter {
by_fields: vec![OutputFields::File],
},
format: Format {
command_as_array: true,
drop_output_field: true,
use_absolute_path: false,
paths_as: PathFormat::Original,
},
},
schema: String::from("4.0"),
@@ -878,7 +923,8 @@ mod test {
intercept: Intercept::default(),
output: Output::Clang {
compilers: vec![],
filter: Filter::default(),
sources: SourceFilter::default(),
duplicates: DuplicateFilter::default(),
format: Format::default(),
},
schema: String::from(SUPPORTED_SCHEMA_VERSION),

View File

@@ -80,11 +80,17 @@ impl OutputWriterImpl {
) -> anyhow::Result<OutputWriterImpl> {
// TODO: This method should fail early if the output file is not writable.
match config {
config::Output::Clang { format, filter, .. } => {
config::Output::Clang {
format,
sources,
duplicates,
..
} => {
let result = ClangOutputWriter {
output: PathBuf::from(&args.file_name),
append: args.append,
filter: filter.clone(),
source_filter: sources.clone(),
duplicate_filter: duplicates.clone(),
command_as_array: format.command_as_array,
formatter: From::from(format),
};
@@ -127,7 +133,8 @@ impl OutputWriter for SemanticOutputWriter {
pub(crate) struct ClangOutputWriter {
output: PathBuf,
append: bool,
filter: config::Filter,
source_filter: config::SourceFilter,
duplicate_filter: config::DuplicateFilter,
command_as_array: bool,
formatter: output::formatter::EntryFormatter,
}
@@ -162,8 +169,11 @@ impl ClangOutputWriter {
entries: impl Iterator<Item = output::clang::Entry>,
) -> anyhow::Result<()> {
// Filter out the entries as per the configuration.
let filter: output::filter::EntryPredicate = TryFrom::try_from(&self.filter)?;
let filtered_entries = entries.filter(filter);
let mut source_filter: output::filter::EntryPredicate = From::from(&self.source_filter);
let mut duplicate_filter: output::filter::EntryPredicate =
From::from(&self.duplicate_filter);
let filtered_entries =
entries.filter(move |entry| source_filter(entry) && duplicate_filter(entry));
// Write the entries to a temporary file.
self.write_into_temporary_compilation_db(filtered_entries)
.and_then(|temp| {

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use std::hash::Hash;
use std::path::PathBuf;
use std::path::Path;
use crate::config;
use crate::output::clang::Entry;
@@ -14,26 +14,33 @@ use builder::EntryPredicateBuilder as Builder;
/// If the predicate returns `false`, the entry is excluded from the result set.
pub type EntryPredicate = Box<dyn FnMut(&Entry) -> bool>;
impl TryFrom<&config::Filter> for EntryPredicate {
type Error = anyhow::Error;
impl From<&config::SourceFilter> for EntryPredicate {
/// Create a filter from the configuration.
fn try_from(config: &config::Filter) -> Result<Self, Self::Error> {
// - Check if the source file exists
// - Check if the source file is not in the exclude list of the configuration
// - Check if the source file is in the include list of the configuration
let source_exist_check =
Builder::filter_by_source_existence(config.source.include_only_existing_files);
let source_paths_to_exclude =
Builder::filter_by_source_paths(&config.source.paths_to_exclude);
let source_paths_to_include =
Builder::filter_by_source_paths(&config.source.paths_to_include);
let source_checks = source_exist_check & !source_paths_to_exclude & source_paths_to_include;
// - Check if the entry is not a duplicate based on the fields of the configuration
let hash_function = create_hash(&config.duplicates.by_fields);
let duplicates = Builder::filter_duplicate_entries(hash_function);
fn from(config: &config::SourceFilter) -> Self {
let source_exist_check = Builder::filter_by_source_existence(config.only_existing_files);
Ok((source_checks & duplicates).build())
let mut builder = Builder::new();
for config::DirectoryFilter { path, ignore } in &config.paths {
let filter = Builder::filter_by_source_path(path);
match ignore {
config::Ignore::Always => {
builder = builder & !filter;
}
config::Ignore::Never => {
builder = builder & filter;
}
}
}
(source_exist_check & builder).build()
}
}
impl From<&config::DuplicateFilter> for EntryPredicate {
/// Create a filter from the configuration.
fn from(config: &config::DuplicateFilter) -> Self {
let hash_function = create_hash(&config.by_fields);
Builder::filter_duplicate_entries(hash_function).build()
}
}
@@ -58,7 +65,7 @@ mod builder {
/// Construct a predicate builder that is empty.
#[inline]
fn new() -> Self {
pub(crate) fn new() -> Self {
Self { candidate: None }
}
@@ -75,13 +82,9 @@ mod builder {
/// Create a predicate that filters out entries
/// that are not using any of the given source paths.
pub(super) fn filter_by_source_paths(paths: &[PathBuf]) -> Self {
if paths.is_empty() {
Self::new()
} else {
let owned_paths: Vec<PathBuf> = paths.to_vec();
Self::from(move |entry| owned_paths.iter().any(|path| entry.file.starts_with(path)))
}
pub(super) fn filter_by_source_path(path: &Path) -> Self {
let owned_path = path.to_owned();
Self::from(move |entry| entry.file.starts_with(owned_path.clone()))
}
/// Create a predicate that filters out entries
@@ -172,13 +175,25 @@ mod builder {
#[cfg(test)]
mod test {
use super::*;
use crate::{vec_of_pathbuf, vec_of_strings};
use crate::vec_of_strings;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
#[test]
fn test_filter_by_source_paths() {
let paths_to_include = vec_of_pathbuf!["/home/user/project/source"];
let paths_to_exclude = vec_of_pathbuf!["/home/user/project/test"];
let config = config::SourceFilter {
only_existing_files: false,
paths: vec![
config::DirectoryFilter {
path: PathBuf::from("/home/user/project/source"),
ignore: config::Ignore::Never,
},
config::DirectoryFilter {
path: PathBuf::from("/home/user/project/test"),
ignore: config::Ignore::Always,
},
],
};
let input: Vec<Entry> = vec![
Entry {
@@ -197,10 +212,7 @@ mod builder {
let expected: Vec<Entry> = vec![input[0].clone()];
let sut: EntryPredicate =
(EntryPredicateBuilder::filter_by_source_paths(&paths_to_include)
& !EntryPredicateBuilder::filter_by_source_paths(&paths_to_exclude))
.build();
let sut: EntryPredicate = From::from(&config);
let result: Vec<Entry> = input.into_iter().filter(sut).collect();
assert_eq!(result, expected);
}

View File

@@ -5,22 +5,23 @@ use crate::{config, semantic};
use anyhow::anyhow;
use path_absolutize::Absolutize;
use std::borrow::Cow;
use std::io;
use std::path::{Path, PathBuf};
pub struct EntryFormatter {
drop_output_field: bool,
use_absolute_path: bool,
path_format: config::PathFormat,
}
impl From<&config::Format> for EntryFormatter {
/// Create a formatter from the configuration.
fn from(config: &config::Format) -> Self {
let drop_output_field = config.drop_output_field;
let use_absolute_path = config.use_absolute_path;
let path_format = config.paths_as.clone();
Self {
drop_output_field,
use_absolute_path,
path_format,
}
}
}
@@ -66,15 +67,17 @@ impl EntryFormatter {
flags,
} => {
let output_clone = output.clone();
let output_result = match output.filter(|_| !self.drop_output_field) {
None => None,
Some(candidate) => {
let x = self.format_path(candidate.as_path(), working_dir)?;
Some(PathBuf::from(x))
}
};
Ok(Entry {
file: PathBuf::from(self.format_path(source.as_path(), working_dir)?),
directory: working_dir.to_path_buf(),
output: output.filter(|_| !self.drop_output_field).and_then(|it| {
// FIXME: Conversion failures are ignored silently.
self.format_path(it.as_path(), working_dir)
.ok()
.map(PathBuf::from)
}),
output: output_result,
arguments: Self::format_arguments(compiler, &source, &flags, output_clone)?,
})
}
@@ -108,15 +111,20 @@ impl EntryFormatter {
Ok(arguments)
}
fn format_path<'a>(&self, path: &'a Path, root: &Path) -> std::io::Result<Cow<'a, Path>> {
if self.use_absolute_path {
fn format_path<'a>(&self, path: &'a Path, root: &Path) -> io::Result<Cow<'a, Path>> {
// Will compute the absolute path if needed.
let absolute = || {
if path.is_absolute() {
path.absolutize()
} else {
path.absolutize_from(root)
}
} else {
Ok(Cow::from(path))
};
match self.path_format {
config::PathFormat::Original => Ok(Cow::from(path)),
config::PathFormat::Absolute => absolute(),
config::PathFormat::Canonical => absolute()?.canonicalize().map(Cow::from),
}
}
}
@@ -135,33 +143,26 @@ mod test {
#[test]
fn test_non_compilations() {
let format = config::Format {
command_as_array: true,
drop_output_field: false,
use_absolute_path: false,
};
let expected: Vec<Entry> = vec![];
let input = semantic::CompilerCall {
compiler: PathBuf::from("/usr/bin/cc"),
working_dir: PathBuf::from("/home/user"),
passes: vec![semantic::CompilerPass::Preprocess],
};
let format = config::Format {
command_as_array: true,
drop_output_field: false,
paths_as: config::PathFormat::Original,
};
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
let expected: Vec<Entry> = vec![];
assert_eq!(expected, result);
}
#[test]
fn test_single_source_compilation() {
let format = config::Format {
command_as_array: true,
drop_output_field: false,
use_absolute_path: false,
};
let input = semantic::CompilerCall {
compiler: PathBuf::from("/usr/bin/clang"),
working_dir: PathBuf::from("/home/user"),
@@ -172,43 +173,34 @@ mod test {
}],
};
let format = config::Format {
command_as_array: true,
drop_output_field: false,
paths_as: config::PathFormat::Original,
};
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
let expected = vec![Entry {
directory: PathBuf::from("/home/user"),
file: PathBuf::from("source.c"),
arguments: vec_of_strings!["/usr/bin/clang", "-Wall", "-o", "source.o", "source.c"],
output: Some(PathBuf::from("source.o")),
}];
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
assert_eq!(expected, result);
}
#[test]
fn test_multiple_sources_compilation() {
let input = compiler_call_with_multiple_passes();
let format = config::Format {
command_as_array: true,
drop_output_field: true,
use_absolute_path: false,
};
let input = semantic::CompilerCall {
compiler: PathBuf::from("clang"),
working_dir: PathBuf::from("/home/user"),
passes: vec![
semantic::CompilerPass::Preprocess,
semantic::CompilerPass::Compile {
source: PathBuf::from("/tmp/source1.c"),
output: Some(PathBuf::from("./source1.o")),
flags: vec_of_strings![],
},
semantic::CompilerPass::Compile {
source: PathBuf::from("../source2.c"),
output: None,
flags: vec_of_strings!["-Wall"],
},
],
paths_as: config::PathFormat::Original,
};
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
let expected = vec![
Entry {
@@ -224,37 +216,20 @@ mod test {
output: None,
},
];
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
assert_eq!(expected, result);
}
#[test]
fn test_multiple_sources_compilation_with_abs_paths() {
let input = compiler_call_with_multiple_passes();
let format = config::Format {
command_as_array: true,
drop_output_field: true,
use_absolute_path: true,
};
let input = semantic::CompilerCall {
compiler: PathBuf::from("clang"),
working_dir: PathBuf::from("/home/user"),
passes: vec![
semantic::CompilerPass::Preprocess,
semantic::CompilerPass::Compile {
source: PathBuf::from("/tmp/source1.c"),
output: Some(PathBuf::from("./source1.o")),
flags: vec_of_strings![],
},
semantic::CompilerPass::Compile {
source: PathBuf::from("../source2.c"),
output: None,
flags: vec_of_strings!["-Wall"],
},
],
paths_as: config::PathFormat::Absolute,
};
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
let expected = vec![
Entry {
@@ -270,9 +245,26 @@ mod test {
output: None,
},
];
let sut: EntryFormatter = (&format).into();
let result = sut.apply(input);
assert_eq!(expected, result);
}
fn compiler_call_with_multiple_passes() -> semantic::CompilerCall {
semantic::CompilerCall {
compiler: PathBuf::from("clang"),
working_dir: PathBuf::from("/home/user"),
passes: vec![
semantic::CompilerPass::Preprocess,
semantic::CompilerPass::Compile {
source: PathBuf::from("/tmp/source1.c"),
output: Some(PathBuf::from("./source1.o")),
flags: vec_of_strings![],
},
semantic::CompilerPass::Compile {
source: PathBuf::from("../source2.c"),
output: None,
flags: vec_of_strings!["-Wall"],
},
],
}
}
}

View File

@@ -37,7 +37,7 @@ impl Builder {
let compilers_to_exclude = match &config.output {
config::Output::Clang { compilers, .. } => compilers
.iter()
.filter(|compiler| compiler.ignore == config::Ignore::Always)
.filter(|compiler| compiler.ignore == config::IgnoreOrConsider::Always)
.map(|compiler| compiler.path.clone())
.collect(),
_ => vec![],

View File

@@ -40,11 +40,11 @@ impl Transform for Transformation {
} = &input;
match self.lookup(compiler) {
Some(config::Compiler {
ignore: config::Ignore::Always,
ignore: config::IgnoreOrConsider::Always,
..
}) => None,
Some(config::Compiler {
ignore: config::Ignore::Conditional,
ignore: config::IgnoreOrConsider::Conditional,
arguments,
..
}) => {
@@ -55,7 +55,7 @@ impl Transform for Transformation {
}
}
Some(config::Compiler {
ignore: config::Ignore::Never,
ignore: config::IgnoreOrConsider::Never,
arguments,
..
}) => {