
Intro
复习
/usr/local目录的作用?install?enable_testing?add_test?set_tests_properties?
今天看一下如何根据不同的实现动态的切换我们代码使用的库(根据可用的系统依赖项更改实现)。
开始
- MathFunctions\CMakeLists.txt
# 设定库的名字和源文件
add_library(MathFunctions MathFunctions.cxx)
# 将当前目录添加到库的包含目录
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if (USE_MYMATH)
# 向程式添加预处理器宏 名为USE_MYMATH
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# 添加库SqrtLibrary
add_library(SqrtLibrary STATIC mysqrt.cxx)
# 链接库tutorial_compiler_flags到SqrtLibrary
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
# 链接库SqrtLibrary到MathFunctions
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
# 检查编译器是否支持log和exp函数
include(CheckCXXSourceCompiles)
# 如何检查呢?编译一个小程序,看是否能通过编译,然后把是否通过编译的结果存到HAVE_LOG和HAVE_EXP里
check_cxx_source_compiles("
#include <cmath>
int main() {
std::log(1.0);
return 0;
}" HAVE_LOG)
check_cxx_source_compiles("
#include <cmath>
int main() {
std::exp(1.0);
return 0;
}" HAVE_EXP)
message(STATUS "HAVE_LOG=${HAVE_LOG}")
message(STATUS "HAVE_EXP=${HAVE_EXP}")
# 如果HAVE_LOG和HAVE_EXP都为真则向SqrtLibrary添加预处理器宏
if(HAVE_LOG AND HAVE_EXP)
# 向SqrtLibrary添加预处理器宏 名为HAVE_LOG和HAVE_EXP
# 程序就可以通过#ifdef HAVE_LOG和#ifdef HAVE_EXP来判断是否支持log和exp函数
target_compile_definitions(SqrtLibrary PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()
endif()
# 如果USE_MYMATH为OFF以上链接不会被执行
# 这里重新链接库tutorial_compiler_flags到MathFunctions 传递编译器标志和其他信息
target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# 设置一个变量名为installable_libs 包含MathFunctions并且传递tutorial_compiler_flags
set(installable_libs MathFunctions tutorial_compiler_flags)
# TARGET 是用来检查某个目标(比如库或可执行文件)是否已经定义的。
# 如果USE_MYMATH为ON则SqrtLibrary一定被定义了
if(TARGET SqrtLibrary)
# 将SqrtLibrary添加到installable_libs
list(APPEND installable_libs SqrtLibrary)
endif()
# 安装installable_libs里的文件
# DESTINATION指定安装的目录
# lib为 /usr/local/lib
install(TARGETS ${installable_libs} DESTINATION lib)
# 安装MathFunctions.h到include目录
# include为 /usr/local/include
install(FILES MathFunctions.h DESTINATION include)
# TARGET 用于安装构建目标,如库和可执行文件。 语法:install(TARGETS target1 target2 ... DESTINATION <dir>)
# 用于安装单个文件或一组文件。 语法:install(FILES file1 file2 ... DESTINATION <dir>)
# `MathFunctions` `tutorial_compiler_flags`都为程式定义的 所以用TARGETS
# `MathFunctions.h`为真实文件 所以用FILES
注意这部分
# 检查编译器是否支持log和exp函数
include(CheckCXXSourceCompiles)
# 如何检查呢?编译一个小程序,看是否能通过编译,然后把是否通过编译的结果存到HAVE_LOG和HAVE_EXP里
check_cxx_source_compiles("
#include <cmath>
int main() {
std::log(1.0);
return 0;
}" HAVE_LOG)
check_cxx_source_compiles("
#include <cmath>
int main() {
std::exp(1.0);
return 0;
}" HAVE_EXP)
message(STATUS "HAVE_LOG=${HAVE_LOG}")
message(STATUS "HAVE_EXP=${HAVE_EXP}")
# 如果HAVE_LOG和HAVE_EXP都为真则向SqrtLibrary添加预处理器宏
if(HAVE_LOG AND HAVE_EXP)
# 向SqrtLibrary添加预处理器宏 名为HAVE_LOG和HAVE_EXP
# 程序就可以通过#ifdef HAVE_LOG和#ifdef HAVE_EXP来判断是否支持log和exp函数
target_compile_definitions(SqrtLibrary PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()
- 通过包含
CheckCXXSourceCompiles引入check_cxx_source_compiles函数。 check_cxx_source_compiles(<code> <resultVar> [FAIL_REGEX <regex1> [<regex2>...]])。- 检查一次是否可以构建
code中提供的源码。结果存储在resultVar指定的内部缓存变量中,布尔值 true 表示成功,布尔值 false 表示失败。
- MathFunctions\mysqrt.cxx
#include "mysqrt.h"
#include <cmath>
#include <iostream>
namespace mathfunctions
{
namespace detail
{
double mysqrt(double x)
{
if (x <= 0)
{
return 0;
}
// 通过使用if defined来判断是否调用
#if defined(HAVE_LOG) && defined(HAVE_EXP)
double result = std::exp(std::log(x) * 0.5);
std::cout << "Computing sqrt of " << x << " to be " << result
<< " using log and exp" << std::endl;
#else
double result = x;
for (int i = 0; i < 10; ++i)
{
if (result <= 0)
{
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
}
#endif
return result;
}
}
}
总结
include(CheckCXXSourceCompiles)check_cxx_source_compiles
真的有夠簡單,就是試著編譯一次我給定的代碼來判斷是否支持,然後使用宏在代碼裏判斷調用就好了。