Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ thiserror = "2.0"
# instructing clang-sys where libclang is located. For some reason, clang-sys does a
# better job at finding libclang this way.
[target.'cfg(not(target_os = "windows"))'.dependencies]
clang = { version="2.0", features=["runtime", "clang_5_0"] }
clang = { version="2.0", features=["runtime", "clang_6_0"] }

# There is an issue with clang 19 and 20 on Windows when using the runtime feature.
# Unloading of DLL causes segfault when the clang::Clang object is droppped.
[target.'cfg(target_os = "windows")'.dependencies]
clang = { version="2.0", features=["clang_5_0"] }
clang = { version="2.0", features=["clang_6_0"] }


[dev-dependencies]
Expand Down
13 changes: 7 additions & 6 deletions src/clangwrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{log, verbose};
use capitalize::Capitalize;
use std::{
path::{Path, PathBuf},
rc::Rc,
sync::{Mutex, MutexGuard, TryLockError},
};

Expand All @@ -16,7 +17,7 @@ static DUMMY_FILE: &str = "mocksmith_dummy_input_file.h";
// Struct to wrap the Clang library and a mutex guard to ensure only one thread can use it
// at a time, at least via this library.
pub(crate) struct ClangWrap {
log: Option<log::Logger>,
log: Rc<Option<log::Logger>>,
clang: clang::Clang,
// After clang::Clang to ensure releasing lock after Clang is dropped
_clang_lock: MutexGuard<'static, ()>,
Expand All @@ -31,7 +32,7 @@ impl ClangWrap {
CLANG_MUTEX.clear_poison();
}

pub(crate) fn new(log: Option<log::Logger>) -> crate::Result<Self> {
pub(crate) fn new(log: Rc<Option<log::Logger>>) -> crate::Result<Self> {
let clang_lock = CLANG_MUTEX.try_lock().map_err(|error| match error {
TryLockError::WouldBlock => crate::MocksmithError::Busy,
TryLockError::Poisoned(_) => MocksmithError::Poisoned,
Expand All @@ -41,12 +42,12 @@ impl ClangWrap {

pub(crate) fn blocking_new() -> crate::Result<Self> {
let clang_lock = CLANG_MUTEX.lock().map_err(|_| MocksmithError::Poisoned)?;
Self::create(clang_lock, None)
Self::create(clang_lock, Rc::new(None))
}

fn create(
clang_lock: MutexGuard<'static, ()>,
log: Option<log::Logger>,
log: Rc<Option<log::Logger>>,
) -> crate::Result<Self> {
let clang = clang::Clang::new().map_err(MocksmithError::ClangError)?;
// Create clang object before getting version to ensure libclang is loaded
Expand Down Expand Up @@ -133,11 +134,11 @@ impl ClangWrap {
.filter(|diagnostic| {
diagnostic.get_severity() >= clang::diagnostic::Severity::Error
})
.for_each(|diagnostic| log!(&self.log, "{}", diagnostic));
.for_each(|diagnostic| log!(self.log, "{}", diagnostic));
} else {
diagnostics
.iter()
.for_each(|diagnostic| verbose!(&self.log, "{}", diagnostic));
.for_each(|diagnostic| verbose!(self.log, "{}", diagnostic));
}

if !self.ignore_errors {
Expand Down
21 changes: 14 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ pub mod naming;

use clangwrap::ClangWrap;
use headerpath::header_include_path;
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
rc::Rc,
};

#[derive(thiserror::Error, Debug, PartialEq)]
pub enum MocksmithError {
Expand Down Expand Up @@ -87,6 +90,7 @@ impl crate::MockHeader {

/// Mocksmith is a struct for generating Google Mock mocks for C++ classes.
pub struct Mocksmith {
log: Rc<Option<log::Logger>>,
clangwrap: ClangWrap,
generator: generate::Generator,

Expand All @@ -102,8 +106,8 @@ impl Mocksmith {
/// The function fails if another thread already holds an instance, since Clang can
/// only be used from one thread.
pub fn new(log_write: Option<Box<dyn std::io::Write>>, verbose: bool) -> Result<Self> {
let log = log_write.map(|write| log::Logger::new(write, verbose));
Self::create(ClangWrap::new(log)?)
let log = Rc::new(log_write.map(|write| log::Logger::new(write, verbose)));
Self::create(Rc::clone(&log), ClangWrap::new(log)?)
}

/// Creates a new Mocksmith instance.
Expand All @@ -117,12 +121,13 @@ impl Mocksmith {
ClangWrap::clear_poison();
clangwrap = ClangWrap::blocking_new();
}
Self::create(clangwrap?)
Self::create(Rc::new(None), clangwrap?)
}

fn create(clangwrap: clangwrap::ClangWrap) -> Result<Self> {
fn create(log: Rc<Option<log::Logger>>, clangwrap: clangwrap::ClangWrap) -> Result<Self> {
let methods_to_mock = MethodsToMockStrategy::AllVirtual;
let mocksmith = Self {
log,
clangwrap,
generator: generate::Generator::new(methods_to_mock),
include_paths: Vec::new(),
Expand Down Expand Up @@ -280,7 +285,8 @@ impl Mocksmith {
}

fn create_mocks(&self, tu: &clang::TranslationUnit) -> Result<Vec<Mock>> {
let classes = model::classes_in_translation_unit(tu, self.methods_to_mock);
let classes =
model::classes_in_translation_unit(Rc::clone(&self.log), tu, self.methods_to_mock);
Ok(classes
.iter()
.filter(|class| (self.filter_class)(class.name.as_str()))
Expand All @@ -299,7 +305,8 @@ mod tests {

#[test]
fn test_new_with_threads() {
let mocksmith = Mocksmith::new(None, false).unwrap();
// new_when_available to wait for other tests using ClangWrap
let mocksmith = Mocksmith::new_when_available().unwrap();

let handle = std::thread::spawn(|| {
assert!(matches!(
Expand Down
9 changes: 5 additions & 4 deletions src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{cell::RefCell, io::Write};
#[macro_export]
macro_rules! log {
($logger:expr, $($arg:tt)*) => {
if let Some(logger) = &$logger {
if let Some(logger) = &*$logger {
logger.log(&format!($($arg)*));
}
};
Expand All @@ -12,7 +12,7 @@ macro_rules! log {
#[macro_export]
macro_rules! verbose {
($logger:expr, $($arg:tt)*) => {
if let Some(logger) = &$logger {
if let Some(logger) = &*$logger {
if logger.verbose {
logger.log(&format!($($arg)*));
}
Expand Down Expand Up @@ -42,6 +42,7 @@ impl Logger {
#[cfg(test)]
mod tests {
use super::*;
use std::rc::Rc;

#[test]
fn macro_doesnt_evaluate_args_if_verbose_disabled() {
Expand All @@ -52,7 +53,7 @@ mod tests {
};

let write = Box::new(Vec::<u8>::new());
let log = Some(Logger::new(write, false));
let log = Rc::new(Some(Logger::new(write, false)));
verbose!(log, "{}", fun());
assert_eq!(calls, 0);
}
Expand All @@ -66,7 +67,7 @@ mod tests {
};

let write = Box::new(Vec::<u8>::new());
let log = Some(Logger::new(write, true));
let log = Rc::new(Some(Logger::new(write, true)));
verbose!(log, "{}", fun());
assert_eq!(calls, 1);
}
Expand Down
Loading
Loading