target_link_libraries(--wrap) 是如何工作的?

How does target_link_libraries(--wrap) work?

我想为 C 代码测试创建模拟函数,并了解到在 target_link_libraries() 中可以选择包装类似于模拟的函数,但不明白它是如何工作的?

target_link_libraries(IntegrationTests crypto-testutils mbedcrypto mbedtls sodium cmocka-static
"-Wl,\
--wrap=ExportCTRKeys,\
--wrap=malloc,\
--wrap=GenRandom)

如何编写此包装功能及其工作原理?

target_link_libraries 获取以 - 开头的参数时,它会将它们视为链接器命令行选项,并将它们原封不动地传递给链接器。因此,这与 CMake 无关,而与 ld 有关。您可以在 ld 参考文档中研究这一点,例如 this one:

--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.

This can be used to provide a wrapper for a system function. The wrapper function should be called "__wrap_symbol". If it wishes to call the system function, it should call "__real_symbol".

Here is a trivial example:

void *
__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu\n", c);
  return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead. The call to "__real_malloc" in "__wrap_malloc" will call the real "malloc" function.

You may wish to provide a "__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as "__wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".

既然是关于CMake的问题,那我从CMake的角度提出一个替代方案。 使用 CMake,您可以构建测试以将要测试的源文件 (SUT) 与测试一起编译,而不是与正在测试的目标链接。

这会增加一些额外的编译时间,因为源文件需要在测试中重新编译。如果这是一个问题,请停止阅读。

假设这对您来说不是问题,那么您可以简单地引入 PUBLIC header 依赖项而不引入它们的实现。然后,您可以为测试文件中使用的依赖项实施 mocks/fakes。

这是一个示例源文件 include/foo.h、foo.c 和 CMakeLists.txt.

#include <ext_decl.h> //Declares struct ext_lib_struct_t in a CMake target ext_decl_lib
int foo(struct ext_lib_struct_t a);
#include <foo.h> //Declares void* foo();
#include <ext_impl.h> //This is the header declaring extlibcall in a CMake target ext_impl_lib
int foo(struct ext_lib_struct_t a) {
   return extlibcall(a); //A mock will be used for this call. 
}
add_library(foo_lib)
target_sources(foo_lib
  PRIVATE
    foo.c
)
target_link_libraries(foo_lib
PUBLIC
    ext_decl_lib
PRIVATE
    ext_impl_lib
)
target_include_directories(foo_lib
  PUBLIC
    include # Where foo.h is.
)

这是您的测试文件,testfoo.c。使用您最喜欢的单元测试框架对其进行自定义。

#include <foo.h>
#include <assert.h>
int test_data;
int extlibcall() {
 return test_data;
}
void testA() { //Replace this with test cases from your favorite unit testing platform.   
   test_data = 2;
   int tr = foo();
   ASSERT(test_data == tr); //Use your favorite assert utility. 
}
int main(void)
{
    testA();
    return 0; 
}

最后是用于测试的 CMake 说明,可以将其放入测试目录中的 CMakeLists.txt 文件中。

add_executable(testlibfoo)
target_sources(testlibfoo
  PRIVATE
    testfoo.c
    $<TARGET_PROPERTY:foo_lib,SOURCE_DIR>/foo.c
)
target_include_directories(test_unity_sall_mgm_join
  PRIVATE
    include
    $<TARGET_PROPERTY:foo_lib,INCLUDE_DIRECTORIES>
)
target_link_libraries(test_unity_sall_mgm_join
  PRIVATE
    assert_lib
    #Add your favorite unit test framework and/or other libraries used in the test
)

测试代码仅编译 foo.c 一次,因为 foo_lib 未链接。但是,当其他库链接到 foo_lib.

时,稍后可能会为您的生产代码重新编译它