Step5:系统检测机制
如果系统有log和exp,我们在mysqrt方法中使用它们。在MathFunctions/CMakeLists.txt中,首先检测它们是否存在,使用的语法是CheckSymbolExits。如果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()
如果存在,使用 target_compile_definitions()
来指定 HAVE_LOG
and HAVE_EXP
as PRIVATE
的编译定义.
if(HAVE_LOG AND HAVE_EXP)
target_compile_definitions(MathFunctions
PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()
命令解释:
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
The INTERFACE
, PUBLIC
and PRIVATE
的关键字指定了下面参数的作用域。PRIVATE
and PUBLIC
items will populate the COMPILE_DEFINITIONS
property of <target>
. PUBLIC
and INTERFACE
items will populate the INTERFACE_COMPILE_DEFINITIONS
property of <target>
. The following arguments specify compile definitions. Repeated calls for the same <target>
append items in the order called.
COMPLIE_DEFINITIONS:编译定义
在mysqrt方法中,可以使用:
#if defined(HAVE_LOG) && defined(HAVE_EXP)
double result = exp(log(x) * 0.5);
std::cout << "Computing sqrt of " << x << " to be " << result
<< " using log and exp" << std::endl;
#else
double result = x;
修改mysqrt.cxx
来包含 cmath
.
#include <cmath>
Step 6:增加一个客制化的命令和产生的文件
在MathFunctions的目录中,一个新的源文件MakeTable.cxx,被用来产生已经计算出来的值。接下来在Math/CMakeLists.txt中,增加恰当的命令来编译可执行的MakeTable和运行它。
MathFunctions/CMakeLists.txt的头部增加:
add_executable(MakeTable MakeTable.cxx)
然后,我们增加一个客制化的命令,运行MakeTable,来产生Table.h.
Then we add a custom command that specifies how to produce Table.h
by running MakeTable.
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
接下来,必须让CMake直到mysqrt.cxxx依赖于这个Table.h.This is done by adding the generated Table.h
to the list of sources for the library MathFunctions.
add_library(MathFunctions
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
我们也必须增加Table.h到目前的二进制目录中,以便于它可以被找到。
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
修改mysqrt.cxx来包含Table.h,接下来就可以重写mysqrt方法来使用这个表。
step 7: 编译和安装
如果我们想发布我们程序给其他人使用。使用CPacke来创建平台指定的安装器,来编译不同平台的二进制安装包等。需要在上一级的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)
这个module将会包含当前平台项目所需要的任何运行时库。
cpack -G ZIP -C Debug
cpack --config CPackSourceConfig.cmake
step 8:Adding Support for a Dashboard
为了支持输出dashboards,在顶级的CMakeLists.txt中,做修改如下:
Replace:
# enable testing
enable_testing()
With:
# enable dashboard scripting
include(CTest)
在顶级目录,我们需要创建一个CTestConfig.cmake的文件,可以指定项目的名称和要提供的dashboard。
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 [-VV] -C Debug -D Experimental
Step 9:混合静态和共享
使用option来定义一个开关,增加一个BUILD_SHARED_LIBS
到顶级CMakeLists.txt中。
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# control where the static and shared libraries are built so that on windows
# we don't need to tinker with the path to run the executable
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 a header file to pass the version number only
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC MathFunctions)
在MathFunctions/CMakeLists.txt中,我们需要根据USE_MyMATH是否打开,来创建一个SqrtLibrary。
# add the library that runs
add_library(MathFunctions MathFunctions.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
# add the command to generate the source code
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
# library that just does sqrt
add_library(SqrtLibrary STATIC
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
# state that we depend on our binary dir to find Table.h
target_include_directories(SqrtLibrary PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
# define the symbol stating we are using the declspec(dllexport) when
# building on windows
target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
# install rules
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/MathFunctions.h
to use dll export defines:
#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);
}
# state that SqrtLibrary need PIC when the default is shared libraries
set_target_properties(SqrtLibrary PROPERTIES
POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
Step 10:增加Generator 表达式
在生成生成系统期间进行评估,以生成特定于每个生成配置的信息;
允许在许多目标属性的上下文中使用,例如链接\库、包含\目录、编译\定义等。在使用命令填充这些属性时,也可以使用它们,例如target\u link\u libraries()、target\u include\u directories()、target\u compile\u definitions()和其他命令。
可用于启用条件链接、编译时使用的条件定义、条件包含目录等。条件可能基于构建配置、目标属性、平台信息或任何其他可查询信息
包括逻辑,信息和输出表达式。
A $<0:...>
results in the empty string, and <1:...>
results in the content of "...". They can also be nested.
下面的代码:
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
可以被替换为:
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
Step 11. 增加导出配置
第一步,更新我们的install(TARGETS)命令不仅仅指定一个DESTINATION,而且指定EXPORT。这EXPORT关键字产生和安装一个CMake 文件包含代码来的导入被列在和安装安装命令在安装树。在MathFunctions/CMakeLists.txt,
我们可以明确使用EXPORT,来导出MathFunctions库,通过更新安装命令。
set(installable_libs MathFunctions tutorial_compiler_flags) if(TARGET SqrtLibrary) list(APPEND installable_libs SqrtLibrary) endif() install(TARGETS ${installable_libs} DESTINATION lib EXPORT MathFunctionsTargets) install(FILES MathFunctions.h DESTINATION include)
现在我们已经导出了MathFunctions,我们也需要明确安装产生的MathFunctionsTargets.cmake文件。通过增加下面到顶层的CMakeLists.txt:
install(EXPORT MathFunctionsTargets FILE MathFunctionsTargets.cmake DESTINATION lib/cmake/MathFunctions )
它将导出一个路径和当前的机器绑定。这个解决方法是更新 MathFunctions target_include_directories()
来理解,它需要不同的INTERFACE位置,当在编译目录和来自一个install/packate。 This means converting the target_include_directories()
call for MathFunctions to look like:
target_include_directories(MathFunctions INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> $<INSTALL_INTERFACE:include> )
这时,CMake已经恰当的打包了目标信息,但是我们仍然需要产生一个MathFunctionsConfig.cmake,以至于CMake可以通过find_package命令来找到我们的项目。增加一个新文件Config.cmake.in,内容如下:
@PACKAGE_INIT@ include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
为了恰当的配置和安装这个文件,增加下面的内容到顶层的CMakeLists.txt:
install(EXPORT MathFunctionsTargets FILE MathFunctionsTargets.cmake DESTINATION lib/cmake/MathFunctions ) include(CMakePackageConfigHelpers) # generate the config file that is includes the exports configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" INSTALL_DESTINATION "lib/cmake/example" NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO ) # generate the version file for the config file write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}" COMPATIBILITY AnyNewerVersion ) # install the configuration file install(FILES ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake DESTINATION lib/cmake/MathFunctions )
此时,我们已经产生了一个可以用于已经安装或者打包的项目的relocatable的CMake配置文件。如果我们想我们的项目,从编译的目录可以被使用。我们在顶层的CMakeLists.txt的底部增加:
export(EXPORT MathFunctionsTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake" )
Step 12:Debug和release
Set CMAKE_DEBUG_POSTFIX
near the beginning of the top-level CMakeLists.txt
file:
set(CMAKE_DEBUG_POSTFIX d) add_library(tutorial_compiler_flags INTERFACE)
And the DEBUG_POSTFIX
property on the tutorial executable:
add_executable(Tutorial tutorial.cxx) set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) target_link_libraries(Tutorial PUBLIC MathFunctions)
Let's also add version numbering to the MathFunctions library. In MathFunctions/CMakeLists.txt
, set the VERSION
and SOVERSION
properties:
set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0") set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
From the Step12
directory, create debug
and release
subbdirectories. The layout will look like:
- Step12 - debug - release
Now we need to setup debug and release builds. We can use CMAKE_BUILD_TYPE
to set the configuration type:
cd debug cmake -DCMAKE_BUILD_TYPE=Debug .. cmake --build . cd ../release cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build .
Now that both the debug and release builds are complete, we can use a custom configuration file to package both builds into a single release. In the Step12
directory, create a file called MultiCPackConfig.cmake
. In this file, first include the default configuration file that was created by the cmake
executable.
Next, use the CPACK_INSTALL_CMAKE_PROJECTS
variable to specify which projects to install. In this case, we want to install both debug and release.
include("release/CPackConfig.cmake") set(CPACK_INSTALL_CMAKE_PROJECTS "debug;Tutorial;ALL;/" "release;Tutorial;ALL;/" )
From the Step12
directory, run cpack
specifying our custom configuration file with the config
option:
cpack --config MultiCPackConfig.cmake