步7
:构建安装器.下一步,假定想要发布
项目,以便其他人
可用.想在多种平台
上发布二进制和源码
.这和第四步
有所不同.
第四步
安装的是从源码
构建的二进制
.本例中,会构建
支持二进制安装和包管理特性
的安装包
.为此,使用CPack
来生成对应平台
的安装器.
即,需要在顶级CMakeLists.txt
底添加几行
:
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include(CPack)
就这样就可以了.通过包含InstallRequiredSystemLibraries来开始.这一模块会包含项目所需
当前平台的运行库
.下一步在存储项目许可和版本信息
的位置,设置一些CPack
变量.
先前已设置好了版本信息
.这一步在顶级源目录
中包含license.txt
.
最后,包含CPackmodule.CPack
模块会用这些变量和当前系统的其他变量
来配置安装器
.
下一步,与正常一样,构建项目
然后运行cpack可执行文件
.
在binary
目录下运行以下命令
以构建二进制发布
:
cpack
用-G
选项来指定生成器
,对多配置构建
,用-C
来指定配置,如:
cpack -G ZIP -C Debug
为了构建源码发布
,可:
cpack --config CPackSourceConfig.cmake
或运行makepackage
,或在IDE
中右键Package
目录然后BuildProject
.
运行二进制
目录中的安装器
,然后运行安装
的可执行文件并验证可运行
.
步8
:增加支持Dashboard
添加把测试
提交到仪表盘
的支持是很简单的.在支持测试
一步中,已给项目定义了一系列测试
.现在只需要运行
这些测试,并提交他们到仪表盘
上即可.
为了支持仪表盘
,在顶级CMakeLists.txt
里包含CTest
模块.
将
//启用测试
enable_testing()
替换为
//启用仪表板脚本
include(CTest)
CTest
模块会自动调用enable_testing()
,所以可从CMake
文件里移除
这一语句.
还要在顶级
目录下(指定项目名
并提交到面板
的目录
),创建CTestConfig.cmake
文件.
set(CTEST_PROJECT_NAME "CMakeTutorial")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
set(CTEST_DROP_SITE_CDASH TRUE)
ctest
可执行文件,会在运行时
读取该文件.可运行cmake
或cmake-gui
来配置
项目,但是不构建
项目,来创建简单面板
.切换到二进制树
目录下然后运行
:
ctest [-VV] -C Debug -D Experimental
或在IDE
中构建Experimental
目标.
ctest
可执行文件会构建和测试
项目,并提交结果
到Kitware
的公共面板:https://my.cdash.org/index.php?project=CMakeTutorial.
步9
:混合静态和共享展示BUILD_SHARED_LIBS
变量是如何控制add_library()
.且允许控制
构建无显式类型(STATIC,SHAREDMODULE
或OBJECT)
的库.
要在顶级CMakeLists.txt
里增加BUILD_SHARED_LIBS
.用option()
命令来让用户可选的开或关
.
下一步要重构MathFunctions
,让它变成封装调用mysqrt
或sqrt
的真实的库
,而非调用代码
来实现逻辑
的库.
也表明USE_MYMATH
不再控制构建MathFunctions
,而是控制库
的行为.
第一步是更新顶级CMakeLists.txt
的第一节如下:
cmake_minimum_required(VERSION 3.10)
//设置项目名和版本
project(Tutorial VERSION 1.0)
//指定`C++`标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
//控制`静态库和共享库`的`构建位置`,这样在`Windows`上就不需要修改运行`可执行文件`的路径
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
//配置头文件为仅传递版本号
configure_file(TutorialConfig.h.in TutorialConfig.h)
//添加`MathFunctions`库
add_subdirectory(MathFunctions)
//添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC MathFunctions)
既然总是使用MathFunctions
.需要更新库的逻辑
.因此在MathFunctions/CMakeLists.txt
里需要创建一个SqrtLibrary
.
该库会在启用USE_MYMATH
时构建并安装
.现在,显式要求按静态库构建SqrtLibrary
就可以了.
结果是MathFunctions/CMakeLists.txt
应该如下:
//添加运行的库
add_library(MathFunctions MathFunctions.cxx)
//说明外部`链接`都要包含当前的`源目录`才能找到`MathFunctions.h`,而我们不必.
target_include_directories(MathFunctions
INTERFACE${CMAKE_CURRENT_SOURCE_DIR}
)
//应该用自己的数学函数吗
option(USE_MYMATH "用自己的" ON)
if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# 先加造表的exe
add_executable(MakeTable MakeTable.cxx)
# 加命令来生成源码
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
# 干活的库
add_library(SqrtLibrary STATIC
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
# 依赖`dir`来找Table.h
target_include_directories(SqrtLibraryPRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
//定义符号,说明在`窗口`上,构建时要用`declspec(dllexport)`
target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
//安装规则
set(installable_libs MathFunctions)
if(TARGET SqrtLibrary)
list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs} DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
下一步更新MathFunctions/mysqrt.cxx
以使用mathfunctions
和detail
名字空间:
#include
#include "MathFunctions.h"
//包括生成的表
#include "Table.h"
namespace mathfunctions {
namespace detail {
//使用简单操作的`hack`平方根计算
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 << "Computing sqrt of " << x << " to be " << result << std::endl;
}
return result;
}
}
}
还要调整tutorial.cxx
,不再使用USE_MYMATH
:
1
.总是包含MathFunctions.h
2
.总是使用mathfunctions::sqrt
3
.不包含cmath
最后更新MathFunctions/MathFunctions.h
来用dll
导出定义:
#if defined(_WIN32)
# if defined(EXPORTING_MYMATH)
# define DECLSPEC __declspec(dllexport)
# else
# define DECLSPEC __declspec(dllimport)
# endif
#else // non windows
# define DECLSPEC
#endif
namespace mathfunctions {
double DECLSPEC sqrt(double x);
}
这时,如果再构建
,会注意到链接
失败,因为试图把包含非位置无关代码
的(PIC)
静态库(指SqrtLibrary
)和另一个包含(PIC)
的库(指MathFunctions
)组合在一起.
不管什么构建类型
,要显式
设置SqrtLibrary
的POSITION_INDEPENDENT_CODE(PIC)
目标属性为True
.
# 默认共享库时,SqrtLibrary需要PIC
set_target_properties(SqrtLibrary PROPERTIES
POSITION_INDEPENDENT_CODE${BUILD_SHARED_LIBS}
)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
练习:修改MathFunctions.h
来使用dll
导出定义.使用CMake
文档,能否用辅助模块
来简化?