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.
时,稍后可能会为您的生产代码重新编译它
我想为 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.
时,稍后可能会为您的生产代码重新编译它