步4
:安装与测试现在开始给项目添加安装规则和支持测试
.
安装规则
非常简单:对MathFunctions
,想安装库和头文件
,对应用
,想安装可执行文件和配置头
.
所以在MathFunctions/CMakeLists.txt
尾添加:
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
在顶层CMakeLists.txt
尾添加:
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
就完成了本地安装.
现在运行cmake
或cmake-gui
来配置项目并构建.
然后在命令行
中运行cmake
的install
选项来执行安装步骤(3.15
引入).
对多配置
工具,记得用--config
来指定配置
.如果用IDE
,直接构建INSTALL
目标即可.这一步会安装适合的头文件,库和可执行文件
:
camke --install .
CMake
的CMAKE_INSTALL_PREFIX
变量确定安装文件的根目录
.如果使用cmake --install
命令,可用--prefix
参数覆盖
安装前缀:
cmake --install . --prefix "/home/myuser/installdir"
浏览安装目录
,然后验证是否可运行安装的Tutorial
.
接着测试
应用,在顶级CMakeLists.txt
尾,可打开测试功能
,然后加一些基本测试
来验证是否正确安装
.
enable_testing()
//是否运行`应用`
add_test(NAME Runs COMMAND Tutorial 25)
//使用消息是否工作?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
//定义简化加测试的函数
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction(do_test)
//测试
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is [-nan|nan|0]")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
对多配置
生成器,要用-C
来指定配置类型
.
ctest -C Debug -VV
ctest -C Release -VV
或也可在IDE
中构建RUN_TESTS
目标.
步5
:增加系统自检现在想在项目
中增加一些目标平台
可能没有依赖
的代码
.如,要加入代码依赖目标平台
是否有log
和exp
函数.
如果平台
有log
和exp
,则就在mysqrt
函数里使用.首先在顶层CMakeLists.txt
里用CheckSymbolExists
来测试
是否有这些函数
?
一些平台,如果没有log
和exp
,就需要连接到m库
.
include(CheckSymbolExists)
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(NOT (HAVE_LOG AND HAVE_EXP))
unset(HAVE_LOG CACHE)
unset(HAVE_EXP CACHE)
set(CMAKE_REQUIRED_LIBRARIES "m")
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(HAVE_LOG AND HAVE_EXP)
target_link_libraries(MathFunctions PRIVATE m)
endif()
endif()
现在给TutorialConfig.h.in
添加一些定义,这样就可在mysqrt.cxx
里使用了:
//平台是否提供`exp`和`log`功能?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
如果在系统上可用log
和exp
,则在mysqrt
里使用它们.在MathFunctions/mysqrt.cxx
里的mysqrt
里添加下述代码(别忘了返回值
前加#endif
):
#if defined(HAVE_LOG) && defined(HAVE_EXP)
double result = exp(log(x) * 0.5);
std::cout << "Computing sqrt of " << x << " to be " << result
<< " 用log和exp" << std::endl;
#else
double result = x;
还要修改mysqrt.cxx
来包含cmath
:
#include
运行cmake
或cmake-gui
来配置项目,然后构建并执行Tutorial
.
会注意到没用log
和exp
,即使认为
它们应该是可用的.很容易发现,在mysqrt.cxx
中忘记包含TutorialConfig.h
了.
也需要更新MathFunctions/CMakeLists.txt
,这样mysqrt.cxx
才可定位文件:
target_include_directories(MathFunctions
INTERFACE${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE${CMAKE_BINARY_DIR}
)
更新后,继续构建
项目,然后运行Tutorial
.如果仍没有使用log
和exp
,打开构建目录
下的生成的Tutorial.h
文件,可能他们在当前系统下不可用的.
那个函数目前结果更好呢,sqrt
还是mysqrt
?
除了在TutorialConfig.h
中存储HAVE_LOG
和HAVE_EXP
外,还有更好的地方
么?试试用target_compile_definitions()
.
首先在TutorialConfig.h
中移除定义,不再需要从mysqrt.cxx
中包含TutorialConfig.h
或在MathFunctions/CMakeLists.txt
中额外包含它了.
接着可把HAVE_LOG
和HAVR_EXP
的检查移动到MathFunctions/CMakeLists.txt
中,然后把这些值
设置为PRIVATE
编译定义.
include(CheckSymbolExists)
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(NOT (HAVE_LOG AND HAVE_EXP))
unset(HAVE_LOG CACHE)
unset(HAVE_EXP CACHE)
set(CMAKE_REQUIRED_LIBRARIES "m")
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(HAVE_LOG AND HAVE_EXP)
target_link_libraries(MathFunctions PRIVATE m)
endif()
endif()
//添加编译定义
if(HAVE_LOG AND HAVE_EXP)
target_compile_definitions(MathFunctions
PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()
调整更新后,重构
项目,再运行Tutorial
并确认结果.
步6
:添加自定义命令和生成文件现在,决定不想再用平台的log
和exp
函数,并想生成一些会在mysqrt
函数里使用到的预计算值表
.
这里,创建该表
并作为构建的一步,然后编译
到应用中.
首先,移除MathFunctions/CMakeLists.txt
中的检查log
和exp
.然后移除mysqrt.cxx
检查对HAVE_LOG
和HAVR_EXP
.同时,也可移除#include
.
在MathFunctions
子目录下,有个MakeTable.cxx
新文件来生成
表格.
浏览该文件
,可发现,表格
是C++
代码生成的,且通过参数传入输出文件名
.
下一步是在MathFunctions/CMakeLists.txt
中添加合适命令
来构建MakeTable
可执行文件,然后作为构建流程的一部分来运行.
需要一些命令
来完成这一步.
首先,在MathFunctions/CMakeLists.txt
的开头,添加MakeTable
为可执行文件
目标.
add_executable(MakeTable MakeTable.cxx)
然后添加一项定义命令
来指定
如何运行MakeTable
创建表格
.
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
接着要让CMake
知道mysqrt.cxx
依赖创建的Table.h
.把生成Table.h
添加到MathFunctions
库的源列表.
add_library(MathFunctions
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
现在使用生成表,首先,修改mysqrt.cxx
来包含Table.h
,然后重写mysqrt
函数以使用这张表
:
double mysqrt(double x)
{
if (x <= 0) {
return 0;
}
//使用该表帮助查找初值
double result = x;
if (x >= 1 && x < 10) {
std::cout << "用表" << std::endl;
result = sqrtTable[static_cast(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 << "计算" << x << "为" << result << std::endl;
}
return result;
}
运行cmake
或cmake-gui
来配置项目并构建.
构建项目
时,首先构建
的是MakeTable
,然后会运行MakeTable
并创建Table.h
.最后编译包含了Table.h
的mysqrt.cxx
来创建MathFunctions
库.
运行Tutorial
可执行文件,然后验证
使用了表格.