├── .gitignore ├── License.txt ├── MathFunctions ├── MathFunctions.h ├── CMakeLists.txt ├── MakeTable.cpp └── mysqrt.cpp ├── TutorialConfig.h.in ├── tutorial.cpp ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-debug -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | This is the open source License.txt file introduced in 2 | CMake/Tests/Tutorial/Step6... 3 | -------------------------------------------------------------------------------- /MathFunctions/MathFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef CMAKE_TUTORIAL_MATHFUNCTIONS_H 2 | #define CMAKE_TUTORIAL_MATHFUNCTIONS_H 3 | 4 | double mysqrt(double x); 5 | 6 | #endif //CMAKE_TUTORIAL_MATHFUNCTIONS_H 7 | -------------------------------------------------------------------------------- /TutorialConfig.h.in: -------------------------------------------------------------------------------- 1 | // 由两个 @ 标注的变量将会被 CMake 替换为版本号 2 | #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ 3 | #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ 4 | 5 | // 如果 USE_MYMATH 选项被选中,CMake 将会导入 USE_MYMATH 宏 6 | #cmakedefine USE_MYMATH 7 | 8 | // 系统提供了 log 和 exp 函数吗? 9 | #cmakedefine HAVE_LOG 10 | #cmakedefine HAVE_EXP -------------------------------------------------------------------------------- /MathFunctions/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 添加生成表的执行程序的构建 2 | add_executable(MakeTable MakeTable.cpp) 3 | # 添加一个使用上面程序输出代码的命令 4 | add_custom_command( 5 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h 6 | COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h 7 | DEPENDS MakeTable 8 | ) 9 | 10 | # 将当前构建目录添加到包含路径以发现 Table.h 11 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 12 | 13 | # 添加主库 14 | add_library(MathFunctions mysqrt.cpp ${CMAKE_CURRENT_BINARY_DIR}/Table.h) 15 | -------------------------------------------------------------------------------- /MathFunctions/MakeTable.cpp: -------------------------------------------------------------------------------- 1 | // A simple program that builds a sqrt table 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | double result; 7 | 8 | // make sure we have enough arguments 9 | if (argc < 2) { 10 | return 1; 11 | } 12 | 13 | // open the output file 14 | FILE *fout = fopen(argv[1], "w"); 15 | if (!fout) { 16 | return 1; 17 | } 18 | 19 | // create a source file with a table of square roots 20 | fprintf(fout, "double sqrtTable[] = {\n"); 21 | for (int i = 0; i < 10; ++i) { 22 | result = sqrt(static_cast(i)); 23 | fprintf(fout, "%g,\n", result); 24 | } 25 | 26 | // close the table with a zero 27 | fprintf(fout, "0};\n"); 28 | fclose(fout); 29 | return 0; 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /tutorial.cpp: -------------------------------------------------------------------------------- 1 | // 一个简单的计算平方根的程序 2 | #include 3 | #include 4 | #include "TutorialConfig.h" 5 | 6 | #ifdef USE_MYMATH 7 | #include "MathFunctions.h" 8 | #else 9 | #include 10 | #endif 11 | 12 | int main(int argc, char *argv[]) { 13 | if (argc < 2) { 14 | fprintf(stdout, "%s Version %d.%d\n", argv[0], 15 | Tutorial_VERSION_MAJOR, 16 | Tutorial_VERSION_MINOR); 17 | fprintf(stdout, "Usage: %s number\n", argv[0]); 18 | return 1; 19 | } 20 | double inputValue = atof(argv[1]); 21 | double outputValue = 0; 22 | 23 | if (inputValue >= 0) { 24 | #ifdef USE_MYMATH 25 | outputValue = mysqrt(inputValue); 26 | #else 27 | outputValue = sqrt(inputValue); 28 | #endif 29 | } 30 | 31 | fprintf(stdout, "The square root of %g is %g\n", 32 | inputValue, outputValue); 33 | return 0; 34 | } -------------------------------------------------------------------------------- /MathFunctions/mysqrt.cpp: -------------------------------------------------------------------------------- 1 | #include "MathFunctions.h" 2 | #include "TutorialConfig.h" 3 | #include 4 | 5 | // 包含生成的文件 6 | #include "Table.h" 7 | 8 | #include 9 | 10 | // 求平方根 11 | double mysqrt(double x) { 12 | if (x <= 0) { 13 | return 0; 14 | } 15 | 16 | double result; 17 | #if defined(HAVE_LOG) && defined(HAVE_EXP) 18 | result = exp(log(x) * 0.5); 19 | fprintf(stdout, "Computing sqrt of %g to be %g using log\n", x, result); 20 | #else 21 | result = x; 22 | // use the table to help find an initial value 23 | if (x >= 1 && x < 10) { 24 | result = sqrtTable[static_cast(x)]; 25 | } 26 | 27 | // 10次迭代 28 | for (int i = 0; i < 10; ++i) { 29 | result = 0.5 * (result + x / result); 30 | fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result); 31 | } 32 | #endif 33 | 34 | return result; 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(CMake_tutorial) 3 | set(CMAKE_CXX_STANDARD 11) 4 | 5 | # 设置有关版本号的两个变量,版本号 1.0 6 | # 这两个变量将会在后面的操作中被插入到源代码中 7 | set(Tutorial_VERSION_MAJOR 1) 8 | set(Tutorial_VERSION_MINOR 0) 9 | 10 | # 提供一个选项:是否使用提供的数学函数? 11 | option(USE_MYMATH "Use tutorial provided math implementation" ON) 12 | include(CMakeDependentOption) 13 | # 提供一个依赖选项,当上一项开启时提供本项,默认开启:是否检查系统提供了 log 和 exp 函数? 14 | CMAKE_DEPENDENT_OPTION(Check_Function "Check whether this system provide the log and exp functions" 15 | ON "USE_MYMATH" OFF) 16 | # 检查并设置相关变量 17 | if (Check_Function) 18 | include(CheckFunctionExists) 19 | check_function_exists(log HAVE_LOG) 20 | check_function_exists(exp HAVE_EXP) 21 | endif (Check_Function) 22 | 23 | # 以 TutorialConfig.h.in 为模版 24 | # 以上设置的变量将会影响此处的处理 25 | # 替换相关变量并输出到 TutorialConfig.h 26 | configure_file( 27 | "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" 28 | "${PROJECT_BINARY_DIR}/TutorialConfig.h" 29 | ) 30 | # 将构建目录添加到 include 的搜索路径中以便找到 TutorialConfig.h 文件 31 | include_directories("${PROJECT_BINARY_DIR}") 32 | 33 | if (USE_MYMATH) 34 | include_directories("${PROJECT_SOURCE_DIR}/MathFunctions") 35 | add_subdirectory(MathFunctions) 36 | set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) 37 | endif (USE_MYMATH) 38 | 39 | set(SOURCE_FILES tutorial.cpp) 40 | add_executable(CMake_tutorial ${SOURCE_FILES}) 41 | target_link_libraries(CMake_tutorial ${EXTRA_LIBS}) 42 | 43 | # 开启测试 44 | # include(CTest) 45 | enable_testing() 46 | 47 | # 程序是否能够运行? 48 | add_test(TutorialRuns CMake_tutorial 25) 49 | 50 | # 使用信息是否正常? 51 | add_test(TutorialUsage CMake_tutorial) 52 | set_tests_properties(TutorialUsage 53 | PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number") 54 | 55 | # 定义一个宏以简化测试的添加 56 | # 使用 PASS_REGULAR_EXPRESSION 测试属性来验证测试的输出是否包含某些字符串 57 | macro(do_test arg result) 58 | add_test(TutorialComp${arg} CMake_tutorial ${arg}) 59 | set_tests_properties(TutorialComp${arg} 60 | PROPERTIES PASS_REGULAR_EXPRESSION ${result}) 61 | endmacro(do_test) 62 | 63 | # 一系列基础测试 64 | do_test(4 "4 is 2") 65 | do_test(9 "9 is 3") 66 | do_test(5 "5 is 2.236") 67 | do_test(7 "7 is 2.645") 68 | do_test(25 "25 is 5") 69 | do_test(-25 "-25 is 0") 70 | do_test(0.0001 "0.0001 is 0.01") 71 | 72 | # build a CPack driven installer package 73 | include (InstallRequiredSystemLibraries) 74 | set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") 75 | set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") 76 | set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") 77 | include (CPack) 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CMake-tutorial([原文](https://cmake.org/cmake-tutorial/)) 2 | 3 | 这份渐进式的教程涵盖了 CMake 帮助处理的一些常见的构建问题。许多议题已经在[《Mastering CMake》](http://www.kitware.com/products/books/CMakeBook.html)中作为独立的话题介绍过,但是了解它们是如何在示例项目中结合在一起的将非常有帮助。你可以在 CMake 源码中的 [Tests/Tutorial](https://gitlab.kitware.com/cmake/cmake/tree/master/Tests/Tutorial) 文件夹找到这份教程,每一步的内容都放置在各自的子文件夹中。 4 | 5 | ## 一个基本的出发点 (Step1) 6 | 7 | 最简单的项目是从源代码文件中构建一个可执行文件,CMakeLists.txt 文件仅需要两行,这将作为我们教程的起点,内容如下: 8 | 9 | ```cmake 10 | cmake_minimum_required (VERSION 2.6) 11 | project (Tutorial) 12 | add_executable(Tutorial tutorial.cxx) 13 | ``` 14 | 15 | 文件中的命令支持大写、小写或者混合使用,这个例子中的命令使用小写。tutorial.cxx 用于计算一个数的平方根,源码的第一版非常简单: 16 | 17 | ```c++ 18 | // 计算一个数的平方根 19 | #include 20 | #include 21 | #include 22 | int main (int argc, char *argv[]) 23 | { 24 | if (argc < 2) 25 | { 26 | fprintf(stdout,"Usage: %s number\n",argv[0]); 27 | return 1; 28 | } 29 | double inputValue = atof(argv[1]); 30 | double outputValue = sqrt(inputValue); 31 | fprintf(stdout,"The square root of %g is %g\n", 32 | inputValue, outputValue); 33 | return 0; 34 | } 35 | ``` 36 | 37 | ### 添加一个版本号并配置头文件 38 | 39 | 你可以直接在源代码中添加版本号,但在 CMakeLists.txt 文件中提供版本号将会更加灵活,我们将文件修改如下: 40 | 41 | ```cmake 42 | cmake_minimum_required (VERSION 2.6) 43 | project (Tutorial) 44 | # 版本号 1.0 45 | set (Tutorial_VERSION_MAJOR 1) 46 | set (Tutorial_VERSION_MINOR 0) 47 | 48 | # 配置一个头文件将一些 CMake 设置传入到源代码中 49 | # 以 TutorialConfig.h.in 为模版,替换相关变量 50 | # 以生成 TutorialConfig.h 51 | configure_file ( 52 | "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" 53 | "${PROJECT_BINARY_DIR}/TutorialConfig.h" 54 | ) 55 | 56 | # 将构建目录添加到 include 的搜索路径中以便找到 57 | # TutorialConfig.h 文件 58 | include_directories("${PROJECT_BINARY_DIR}") 59 | 60 | # 添加可执行文件 61 | add_executable(Tutorial tutorial.cxx) 62 | ``` 63 | 64 | 因为配置文件将会写入到构建目录中,所以我们将这个目录添加到包含文件的搜索路径中。在源代码中添加 TutorialConfig.h.in 文件: 65 | 66 | ```c++ 67 | // the configured options and settings for Tutorial 68 | #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ 69 | #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ 70 | ``` 71 | 72 | 当 CMake 生成这个头文件时,@Tutorial_VERSION_MAJOR@ 和 @Tutorial_VERSION_MINOR@ 的值将会由 CMakeLists.txt 中对应的值替换。接下来我们将头文件包含到 tutorial.cxx 中并且使用这个版本号,代码如下: 73 | 74 | ```c++ 75 | // A simple program that computes the square root of a number 76 | #include 77 | #include 78 | #include 79 | #include "TutorialConfig.h" 80 | 81 | int main (int argc, char *argv[]) 82 | { 83 | if (argc < 2) 84 | { 85 | fprintf(stdout,"%s Version %d.%d\n", 86 | argv[0], 87 | Tutorial_VERSION_MAJOR, 88 | Tutorial_VERSION_MINOR); 89 | fprintf(stdout,"Usage: %s number\n",argv[0]); 90 | return 1; 91 | } 92 | double inputValue = atof(argv[1]); 93 | double outputValue = sqrt(inputValue); 94 | fprintf(stdout,"The square root of %g is %g\n", 95 | inputValue, outputValue); 96 | return 0; 97 | } 98 | ``` 99 | 100 | 主要的改变是包含了头文件并且在使用方法信息中打印了版本号。 101 | 102 | ## 添加一个库 (Step 2) 103 | 104 | 现在我们要在项目中添加一个库,这个库将会包含我们自己的计算平方根的实现。可执行文件将可以使用这个库,而不是使用编译器提供的平方根标准方法。本教程中将这个库放到名为 MathFunctions 的子文件夹中,这个子文件夹需要包含一个 CMakeLists.txt 文件,文件中有如下一行: 105 | 106 | ```cmake 107 | add_library(MathFunctions mysqrt.cxx) 108 | ``` 109 | 110 | mysqrt.cxx 文件中有一个叫做 mysqrt 的函数,它提供与编译器的 sqrt 函数相同的功能。我们在顶层的 CMakeLists.txt 中添加一个 add_subdirectory 调用以构建这个库。为了找到 MathFunctions/MathFunctions.h 头文件中的函数原型,我们添加另一条包含路径。最后一个改动是将这个库添加到可执行文件中。顶层 CMakeLists.txt 文件中添加的最新几行如下: 111 | 112 | ```cmake 113 | include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions") 114 | add_subdirectory (MathFunctions) 115 | 116 | # add the executable 117 | add_executable (Tutorial tutorial.cxx) 118 | target_link_libraries (Tutorial MathFunctions) 119 | ``` 120 | 121 | 考虑一下将这个库设计为可选的,本教程中这样做也许是不必要的,但是当使用更大的库或者第三方的库时你也许会用到。第一步是在顶层的 CMakeLists.txt 中添加一个选择: 122 | 123 | ```cmake 124 | # 是否使用我们自己的函数? 125 | option (USE_MYMATH 126 | "Use tutorial provided math implementation" ON) 127 | ``` 128 | 129 | CMake GUI 中将会显示一个 ON 的默认值,用户可以按需更改。这个设置将会被缓存,这样在每次对这个项目运行 CMake 时用户不需要再次设置。接下来的更改是将 MathFunctions 库的构建和连接设置为可选的,我们在顶层 CMakeLists.txt 的最后修改如下: 130 | 131 | ```cmake 132 | # add the MathFunctions library? 133 | if (USE_MYMATH) 134 | include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions") 135 | add_subdirectory (MathFunctions) 136 | set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) 137 | endif (USE_MYMATH) 138 | 139 | # add the executable 140 | add_executable (Tutorial tutorial.cxx) 141 | target_link_libraries (Tutorial ${EXTRA_LIBS}) 142 | ``` 143 | 144 | 这将根据 USE_MYMATH 的设置来决定是否编译并使用 MathFunctions 库。注意这里使用了一个 EXTRA_LIBS 变量来收集任何可选的库,以在之后链接到可执行文件中。对有许多可选组件的项目,这是一种保持其整洁的常用方法。相应的源代码更改如下: 145 | 146 | ```c++ 147 | / A simple program that computes the square root of a number 148 | #include 149 | #include 150 | #include 151 | #include "TutorialConfig.h" 152 | #ifdef USE_MYMATH 153 | #include "MathFunctions.h" 154 | #endif 155 | 156 | int main (int argc, char *argv[]) 157 | { 158 | if (argc < 2) 159 | { 160 | fprintf(stdout,"%s Version %d.%d\n", argv[0], 161 | Tutorial_VERSION_MAJOR, 162 | Tutorial_VERSION_MINOR); 163 | fprintf(stdout,"Usage: %s number\n",argv[0]); 164 | return 1; 165 | } 166 | 167 | double inputValue = atof(argv[1]); 168 | 169 | #ifdef USE_MYMATH 170 | double outputValue = mysqrt(inputValue); 171 | #else 172 | double outputValue = sqrt(inputValue); 173 | #endif 174 | 175 | fprintf(stdout,"The square root of %g is %g\n", 176 | inputValue, outputValue); 177 | return 0; 178 | } 179 | ``` 180 | 181 | 在源代码中我们同样使用了 USE_MYMATH 变量。通过在 TutorialConfig.h.in 中添加如下配置,Cmake 将这个变量引入到源代码中: 182 | 183 | ```c++ 184 | #cmakedefine USE_MYMATH 185 | ``` 186 | 187 | ## 安装与测试 (Step 3) 188 | 189 | 接下来我们在项目中添加安装规则和测试支持。安装规则非常直接,对于 MathFunctions 库的安装,我们在 MathFunctions 的 CMakeLists.txt 中添加如下几行: 190 | 191 | ```cmake 192 | install (TARGETS MathFunctions DESTINATION bin) 193 | install (FILES MathFunctions.h DESTINATION include) 194 | ``` 195 | 196 | 对于应用程序可执行文件和头文件的安装,我们在顶层的 CMakeLists.txt 中添加如下几行: 197 | 198 | ```cmake 199 | # add the install targets 200 | install (TARGETS Tutorial DESTINATION bin) 201 | install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" 202 | DESTINATION include) 203 | ``` 204 | 205 | 万事俱备,接下来你应该可以构建这个项目,然后键入 `make install` (或者在 IDE 中构建 INSTALL 目标),它将会安装合适的头文件,库和执行文件。CMake 的 CMAKE_INSTALL_PREFIX 变量用于决定文件安装位置的根。添加测试也是一个同样直接的过程。在顶层 CMakeLists.txt 的结尾,我们可以添加几个基础测试以判别程序是否工作正常: 206 | 207 | ```camke 208 | include(CTest) 209 | 210 | # does the application run 211 | add_test (TutorialRuns Tutorial 25) 212 | 213 | # does it sqrt of 25 214 | add_test (TutorialComp25 Tutorial 25) 215 | set_tests_properties (TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5") 216 | 217 | # does it handle negative numbers 218 | add_test (TutorialNegative Tutorial -25) 219 | set_tests_properties (TutorialNegative PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0") 220 | 221 | # does it handle small numbers 222 | add_test (TutorialSmall Tutorial 0.0001) 223 | set_tests_properties (TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01") 224 | 225 | # does the usage message work? 226 | add_test (TutorialUsage Tutorial) 227 | set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number") 228 | ``` 229 | 230 | 构建完成后,可以使用命令行工具 `ctest` 运行测试。第一个测试只是验证程序是否运行,没有段错误或其他的崩溃,并返回零值。这是一个 CTest 测试的基本形式。接下来的几个测试都使用 PASS_REGULAR_EXPRESSION 测试属性来验证测试的输出是否包含某些字符串。这样用来验证预期的计算结果,并且当参数数目不正确时打印使用信息。如果你想添加大量测试来测试不同的输入值,你可能会考虑创建如下所示的宏: 231 | 232 | ```cmake 233 | #define a macro to simplify adding tests, then use it 234 | macro (do_test arg result) 235 | add_test (TutorialComp${arg} Tutorial ${arg}) 236 | set_tests_properties (TutorialComp${arg} 237 | PROPERTIES PASS_REGULAR_EXPRESSION ${result}) 238 | endmacro (do_test) 239 | 240 | # do a bunch of result based tests 241 | do_test (25 "25 is 5") 242 | do_test (-25 "-25 is 0") 243 | ``` 244 | 245 | 每调用一次 do_test,根据传递的参数,都会添加一个拥有名字、输入和输出的测试。 246 | 247 | ## 添加系统自检 (Step 4) 248 | 249 | 接下来让我们向项目中添加一些代码,这些代码依赖的功能目标平台可能没有提供。这个例子中,我们添加的代码依赖于目标平台是否提供了对数 log 和指数 exp 函数。当然几乎所有的平台都提供了这样的函数,本教程假定它们是不常见的功能。如果平台提供了 log,那么我们可以在 mysqrt 函数中使用它计算平方根。我们首先使用顶层 CMakeLists.txt 文件中的 CheckFunctionExists.cmake 宏来测试这些功能的可用性,如下所示: 250 | 251 | ```cmake 252 | # does this system provide the log and exp functions? 253 | include (CheckFunctionExists) 254 | check_function_exists (log HAVE_LOG) 255 | check_function_exists (exp HAVE_EXP) 256 | ``` 257 | 258 | 当 CMake 在平台上发现它们时,我们在 TutorialConfig.h.in 中定义这些值: 259 | 260 | ```cmake 261 | // does the platform provide exp and log functions? 262 | #cmakedefine HAVE_LOG 263 | #cmakedefine HAVE_EXP 264 | ``` 265 | 266 | log 和 exp 的测试需要放在 configure_file 命令之前,configure_file 命令会立即使用 CMake 中的当前设置生成文件。当系统提供了这两个函数时,我们可以使用以下代码在 mysqrt 函数中提供一个基于 log 和 exp 的替代实现: 267 | 268 | ```c++ 269 | // if we have both log and exp then use them 270 | #if defined (HAVE_LOG) && defined (HAVE_EXP) 271 | result = exp(log(x)*0.5); 272 | #else // otherwise use an iterative approach 273 | . . . 274 | ``` 275 | 276 | ## 添加生成文件和生成器 (Step 5) 277 | 278 | 在本节中,我们将展示如何将生成的源文件添加到应用程序的构建过程中。本例中,我们将会在构建过程中创建一个预先计算的平方根表,然后将这张表编译进我们的程序中。我们首先需要一个生成这张表的程序,为此我们在 MathFunctions 的子文件夹中添加一个新的名为 MakeTable.cxx 的源文件: 279 | 280 | ```c++ 281 | // A simple program that builds a sqrt table 282 | #include 283 | #include 284 | #include 285 | 286 | int main (int argc, char *argv[]) 287 | { 288 | int i; 289 | double result; 290 | 291 | // make sure we have enough arguments 292 | if (argc < 2) 293 | { 294 | return 1; 295 | } 296 | 297 | // open the output file 298 | FILE *fout = fopen(argv[1],"w"); 299 | if (!fout) 300 | { 301 | return 1; 302 | } 303 | 304 | // create a source file with a table of square roots 305 | fprintf(fout,"double sqrtTable[] = {\n"); 306 | for (i = 0; i < 10; ++i) 307 | { 308 | result = sqrt(static_cast(i)); 309 | fprintf(fout,"%g,\n",result); 310 | } 311 | 312 | // close the table with a zero 313 | fprintf(fout,"0};\n"); 314 | fclose(fout); 315 | return 0; 316 | } 317 | ``` 318 | 319 | 注意这张表会以有效的 C++ 代码的形式生成,输出文件的名字以参数的形式提供。接下来向 MathFunctions 的 CMakeLists.txt 文件中添加合适的命令以构建 MakeTable 的可执行文件,并运行它作为构建过程的一部分。需要几个命令来完成此操作,如下所示: 320 | 321 | ```cmake 322 | # first we add the executable that generates the table 323 | add_executable(MakeTable MakeTable.cxx) 324 | 325 | # add the command to generate the source code 326 | add_custom_command ( 327 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h 328 | COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h 329 | DEPENDS MakeTable 330 | ) 331 | 332 | # add the binary tree directory to the search path for 333 | # include files 334 | include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) 335 | 336 | # add the main library 337 | add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h ) 338 | ``` 339 | 340 | 首先像添加其他执行文件那样添加 MakeTable 的执行文件。然后我们添加一个自定义命令以使用 MakeTable 生成 Table.h 文件。接下来我们需要将生成的文件添加到 MathFunctions 库的源文件列表中,以让 CMake 知道 mysqrt.cxx 依赖于 Table.h 文件。我们也需要将当前的构建文件夹添加到包含文件列表中,以让 Table.h 文件可以被发现并包含到 mysqrt.cxx 中。当构建这个项目时,它会先构建 MakeTable 的执行文件,然后运行 MakeTable 生成 Table.h 文件,最后它会编译包含有 Table.h 的 mysqrt.cxx 以生成 MathFunctions 库。此时,顶层 CMakeLists.txt 文件如下所示: 341 | 342 | ```cmake 343 | cmake_minimum_required (VERSION 2.6) 344 | project (Tutorial) 345 | include(CTest) 346 | 347 | # The version number. 348 | set (Tutorial_VERSION_MAJOR 1) 349 | set (Tutorial_VERSION_MINOR 0) 350 | 351 | # does this system provide the log and exp functions? 352 | include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake) 353 | 354 | check_function_exists (log HAVE_LOG) 355 | check_function_exists (exp HAVE_EXP) 356 | 357 | # should we use our own math functions 358 | option(USE_MYMATH 359 | "Use tutorial provided math implementation" ON) 360 | 361 | # configure a header file to pass some of the CMake settings 362 | # to the source code 363 | configure_file ( 364 | "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" 365 | "${PROJECT_BINARY_DIR}/TutorialConfig.h" 366 | ) 367 | 368 | # add the binary tree to the search path for include files 369 | # so that we will find TutorialConfig.h 370 | include_directories ("${PROJECT_BINARY_DIR}") 371 | 372 | # add the MathFunctions library? 373 | if (USE_MYMATH) 374 | include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions") 375 | add_subdirectory (MathFunctions) 376 | set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) 377 | endif (USE_MYMATH) 378 | 379 | # add the executable 380 | add_executable (Tutorial tutorial.cxx) 381 | target_link_libraries (Tutorial ${EXTRA_LIBS}) 382 | 383 | # add the install targets 384 | install (TARGETS Tutorial DESTINATION bin) 385 | install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" 386 | DESTINATION include) 387 | 388 | # does the application run 389 | add_test (TutorialRuns Tutorial 25) 390 | 391 | # does the usage message work? 392 | add_test (TutorialUsage Tutorial) 393 | set_tests_properties (TutorialUsage 394 | PROPERTIES 395 | PASS_REGULAR_EXPRESSION "Usage:.*number" 396 | ) 397 | 398 | 399 | #define a macro to simplify adding tests 400 | macro (do_test arg result) 401 | add_test (TutorialComp${arg} Tutorial ${arg}) 402 | set_tests_properties (TutorialComp${arg} 403 | PROPERTIES PASS_REGULAR_EXPRESSION ${result} 404 | ) 405 | endmacro (do_test) 406 | 407 | # do a bunch of result based tests 408 | do_test (4 "4 is 2") 409 | do_test (9 "9 is 3") 410 | do_test (5 "5 is 2.236") 411 | do_test (7 "7 is 2.645") 412 | do_test (25 "25 is 5") 413 | do_test (-25 "-25 is 0") 414 | do_test (0.0001 "0.0001 is 0.01") 415 | ``` 416 | 417 | TutorialConfig.h.in 如下: 418 | 419 | ```c++ 420 | // the configured options and settings for Tutorial 421 | #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ 422 | #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ 423 | #cmakedefine USE_MYMATH 424 | 425 | // does the platform provide exp and log functions? 426 | #cmakedefine HAVE_LOG 427 | #cmakedefine HAVE_EXP 428 | ``` 429 | 430 | MathFunctions 的 CMakeLists.txt 文件如下: 431 | 432 | ```cmake 433 | # first we add the executable that generates the table 434 | add_executable(MakeTable MakeTable.cxx) 435 | # add the command to generate the source code 436 | add_custom_command ( 437 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h 438 | DEPENDS MakeTable 439 | COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h 440 | ) 441 | # add the binary tree directory to the search path 442 | # for include files 443 | include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) 444 | 445 | # add the main library 446 | add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h) 447 | 448 | install (TARGETS MathFunctions DESTINATION bin) 449 | install (FILES MathFunctions.h DESTINATION include) 450 | ``` 451 | 452 | ## 构建安装程序 (Step 6) 453 | 454 | 接下来假设我们想将我们的项目分发给其他人,以便他们可以使用它。我们希望在各种平台上提供二进制和源代码分发。这与之前的第三步有些不同,在这个例子中,我们将构建安装包以支持二进制安装和包管理功能,比如 cygwin,debian,RPMs 等。我们将会使用 CPack 来创建平台相关的安装程序。具体来说,我们需要在我们的顶层 CMakeLists.txt 文件的底部添加几行: 455 | 456 | ```cmake 457 | # build a CPack driven installer package 458 | include (InstallRequiredSystemLibraries) 459 | set (CPACK_RESOURCE_FILE_LICENSE 460 | "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") 461 | set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") 462 | set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") 463 | include (CPack) 464 | ``` 465 | 466 | 我们从包含 InstallRequiredSystemLibraries 开始。该模块包含有这个项目在当前平台所需的任何运行时库。然后我们设置一些 CPack 变量指明此项目许可证和版本信息的位置。版本信息使用了本教程前面设置的变量。最后我们包含了 CPack 模块,它将使用你设置的这些变量和其他系统属性来配置安装程序。 467 | 468 | 接下来就是按照通常的方法构建项目,然后运行 CPack 命令。要构建一个二进制分发,你可以运行: 469 | 470 | ```bash 471 | cpack --config CPackConfig.cmake 472 | ``` 473 | 474 | 要创建一个源码分发,你可以键入: 475 | 476 | ```bash 477 | cpack --config CPackSourceConfig.cmake 478 | ``` 479 | 480 | ## 添加对仪表板的支持 (Step 7) 481 | 482 | 添加将测试结果提交给仪表板的支持非常简单。在教程之前的步骤中已经定义了一些测试,我们只需运行这些测试并且将它们提交给一个仪表板。要包括对仪表板的支持,我们将 CTest 模块包含在我们的顶层 CMakeLists.txt 文件中: 483 | 484 | ```cmake 485 | # enable dashboard scripting 486 | include (CTest) 487 | ``` 488 | 489 | 我们还创建一个CTestConfig.cmake文件,可以在该文件中为仪表板指定此项目的名称。 490 | 491 | ```cmake 492 | set (CTEST_PROJECT_NAME "Tutorial") 493 | ``` 494 | 495 | 当运行 CTest 时它会读取这个文件。要创建简单的仪表板,你可以在项目中运行 CMake,然后切换目录到构建目录中运行 `ctest –D Experimental`. 仪表板的结构将会上传到 Kitware 的公共仪表板中([这里](http://www.cdash.org/CDash/index.php?project=PublicDashboard))。 --------------------------------------------------------------------------------