文章目录
引言(Introduction)
根据百度百科的解释,CMake是跨平台的免费的开源的构建工具,可以用简单的语句来描述代码在所有平台的构建过程,实现代码自动构建(build automation)的功能。所谓自动构建是指软件自行构造的过程,包括编译计算机源代码为二进制代码,打包二进制代码以及执行自动测试的过程。CMake能够输出各种各样的makefile或者project文件,并且能够测试编译器所支持的C++特性,类似于UNIX下的automake。
需要注意的是,使用CMake构建程序需要经过两个步骤:1. 根据配置文件(CMakeLists.txt)产生标准的构建文件;2. 平台自带的构建工具根据构建文件执行真正的构建过程。从这里可以看出来,CMake真正发挥作用的是第一个步骤,并且其需要能够根据不同的平台,产生不同的构建文件,从而实现跨平台构建的功能。
所以真正需要学习的是CMakeLists.txt文件的编写,该文件也被称为组态档,并且该文件是用CMake language编写的。所以下面首先介绍CMakeLists.txt的一些特性,随后根据一个简单例子介绍CMakeLists.txt编写的要点。
1. CMakeLists.txt
CMakeLists.txt是CMake的核心内容,其内包含cmake的版本信息以及编译器需要的各种信息。CMakeLists.txt是根据一个相对简单,可解释性强的脚本语言进行编写的。该语言支持变量,字符串操作,数组,函数声明以及模块导入的功能。其命令的形式主要如下:
COMMAND(argument ...)
其中参数之间使用空格分开。
变量的取值使用 , 但 是 在 I F 控 制 语 句 中 则 是 直 接 使 用 变 量 名 。 环 境 变 量 使 用 {},但是在IF控制语句中则是直接使用变量名。环境变量使用 ,但是在IF控制语句中则是直接使用变量名。环境变量使用ENV{}方式取值,使用SET(ENV{VAR} VALUE)赋值。
需要注意的就这些,如果后续笔者看到还有别的需要注意的点,笔者会持续进行更新。
2. An Example
本例子来自知乎up主(被包养的程序员丶)的文章,感谢其分享。后续实验是根据官网上进行复现的,官网的tutorial也有Github。
2.1 基础
-
准备计算平方根的代码,命名为calculatesqrt.cpp;
// ./calculatesqrt.cpp #include <stdio.h> #include <stdlib.h> #include <math.h> int main(int argc, char* argv[]){ if(argc<2){ fprintf(stdout, "Uage: %s number\n", argv[0]); return 1; } double inputValue = atof(argv[1]); double outputValue = sqrt(inputValue); fprintf(stdout, "The square root of %g is %g\n",inputValue, outputValue); return 0; }
-
编写相应的CMakeLists.txt文件;
# ./CMakeLists.txt # specify the minimum cmake version cmake_minimum_required(VERSION 3.10) # set the project name project(CalculateSqrt) # add the executable add_executable(CalculateSqrt calculatesqrt.cpp)
-
创建build目录。
此时得到的目录树如下:
完成上面三步后,进入build目录,执行cmake命令:
这时得到的文件目录如下:
到这一步就已经完成CMake的工作了,但要生成可执行文件还得执行make命令:
此时得到的目录树如下:
上图中,CalculateSqrt就是得到的可执行文件。执行可执行文件CalculateSqrt可以得到:
2.2 为项目添加版本号和可执行文件
在进行这一步前,可以把build目录下面的所有文件都删除,并保留build目录。
-
在源代码目录中,添加输入文件CalculateSqrtConfig.h.in作为头文件的配置文件:
// ./CalculateSqrtConfig.h.in // the configured options and settings for CalculateSqrt, #define CalculateSqrt_VERSION_MAJOR @CalculateSqrt_VERSION_MAJOR@ #define CalculateSqrt_VERSION_MINOR @CalculateSqrt_VERSION_MINOR@
-
修改calculatesqrt.cpp:
// ./calculatesqrt.cpp #include <stdio.h> #include <stdlib.h> #include <string> #include <iostream> #include <math.h> #include "CalculateSqrtConfig.h" int main(int argc, char* argv[]){ if(argc<2){ std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." << CalculateSqrt_VERSION_MINOR << std::endl; fprintf(stdout, "Uage: %s number\n", argv[0]); return 1; } double inputValue = atof(argv[1]); double outputValue = sqrt(inputValue); fprintf(stdout, "The square root of %g is %g\n",inputValue, outputValue); return 0; }
-
修改CMakeLists.txt:
# ./CMakeLists.txt # specify the minimum cmake version cmake_minimum_required(VERSION 3.10) # set the project name and version project(CalculateSqrt VERSION 1.0) # copy a file to another location and modify its contents # https://cmake.org/cmake/help/latest/command/configure_file.html configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) # add the executable add_executable(CalculateSqrt calculatesqrt.cpp) # add include directories to a target target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}")
-
进入build目录,执行cmake命令,可以生成得到MakeFile文件与CalculateSqrtConfig.h文件:
自动生成的CalculateSqrtConfig.h文件内容如下:
// ./build/CalculateSqrtConfig.h // the configured options and settings for CalculateSqrt, #define CalculateSqrt_VERSION_MAJOR 1 #define CalculateSqrt_VERSION_MINOR 0
可见其是把CMake里面的变量取了出来。
这一步如果出现Cannot specify include directories for target "CalculateSqrt" which is not built by this project.
那么需要修改包含头文件目录的命令为:
# target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}") set_target_properties(CalculateSqrt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}")
-
执行make命令得到可执行文件:
-
执行CalculateSqrt可以得到输出:
???这输出不对劲呀,不是cpp文件的内容。贼迷。
2.3 为工程添加链接库lib
在进行这一步前,可以把build目录下面的所有文件都删除,并保留build目录。这一步是链接已有的函数到工程。
-
在源代码目录下创建MathFunctions目录,并在其下面定义三个文件MathFunctions.h,mysqrt.cpp,CMakeLists.txt,其内容分别如下:
// ./MathFunctions/MathFunctions.h #include <stdio.h> #include <math.h> // declare your sqrt function double mysqrt(double);
mysqrt.cpp内容如下
// ./MathFunctions/mysqrt.cpp #include "MathFunctions.h" double mysqrt(double inputValue){ return sqrt(inputValue); }
CMakeLists.txt如下:
# ./MathFunctions/CMakeLists.txt add_library(MathFunctions mysqrt.cpp)
-
修改源代码目录下的CMakeLists.txt文件为:
# CMakeLists.txt # specify the minimum cmake version cmake_minimum_required(VERSION 3.10) # set the project name and version project(CalculateSqrt VERSION 1.0) # copy a file to another location and modify its contents # https://cmake.org/cmake/help/latest/command/configure_file.html configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) add_subdirectory(MathFunctions) # add the executable add_executable(CalculateSqrt calculatesqrt.cpp) # specify libraries of flags to use when linking a given target and/or its dependents # https://cmake.org/cmake/help/latest/command/target_link_libraries.html target_link_libraries(CalculateSqrt PUBLIC MathFunctions) # add include directories to a target target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions")
-
进入build目录下,执行cmake命令可以得到:
这时候CMakeLists.txt文件生成成功。
-
使用make构建可执行文件:
这时候文件目录的结构为:
-
执行可执行文件可以得到正常的结果了:
-
Plus:为了让MathFunctions库变为可选的,即通过简单修改CMakeLists.txt的文件,达到变更使用MathFunctions的效果,这在大项目里面是非常有用的。
为了实现lib变为可选的功能,只需要修改源代码目录下的calculatesqrt.cpp与CMakeLists.txt文件即可。最终得到的CMakeLists.txt文件内容如下:# ./CMakeLists.txt # specify the minimum cmake version cmake_minimum_required(VERSION 3.10) # set the project name and version project(CalculateSqrt VERSION 1.0) # make the MathFunctions library optional option(USE_MYMATH "Use tutorial provided math implementation" ON) # copy a file to another location and modify its contents # https://cmake.org/cmake/help/latest/command/configure_file.html configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) if(USE_MYMATH) add_subdirectory(MathFunctions) # use the variable EXTRA_LIBS to collect up any optional libraries list(APPEND EXTRA_LIBS MathFunctions) # use the variable EXTRA_INCLUDES to collect up any optional header files list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions") endif() # add the executable add_executable(CalculateSqrt calculatesqrt.cpp) # specify libraries of flags to use when linking a given target and/or its dependents # https://cmake.org/cmake/help/latest/command/target_link_libraries.html target_link_libraries(CalculateSqrt PUBLIC MathFunctions) # add include directories to a target target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions")
calculatesqrt.cpp的内容如下:
// ./calculatesqrt.cpp #include <stdio.h> #include <stdlib.h> #include <string> #include <iostream> #include <math.h> #include "CalculateSqrtConfig.h" #ifdef USE_MYMATH #include "MathFunctions.h" #endif int main(int argc, char* argv[]){ if(argc<2){ std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." << CalculateSqrt_VERSION_MINOR << std::endl; fprintf(stdout, "Uage: %s number\n", argv[0]); return 1; } double inputValue = atof(argv[1]); #ifdef USE_MYMATH double outputValue = mysqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << " with mysqrt function" << std::endl; #else double outputValue = sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; #endif return 0; }
2.4 为库添加使用需求
可以通过修改CMakeLists.txt文件来满足库的不同的使用需求,cmake中控制不同库的使用需求的命令为:
- target_compile_definitions()
- target_compile_options()
- target_include_directories()
- target_link_libraries()
下面将从最简单的接口功能介绍这一部分。接口(INTERFACE)是生产者为消费者提供的函数入口,而消费者并不需要知道其内部是如何实现的。这一个功能的控制需要使用到target_include_directories()的命令。
这一部分只需要修改两个文件:"./CMakeLists.txt"与"./MathFunctions/CMakeLists.txt"。
-
修改"./MathFunctions/CMakeLists.txt",提供接口:
# ./MathFunctions/CMakeLists.txt add_library(MathFunctions mysqrt.cpp) target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
-
修改"./CMakeLists.txt",添加库:
# ./CMakeLists.txt # specify the minimum cmake version cmake_minimum_required(VERSION 3.10) # set the project name and version project(CalculateSqrt VERSION 1.0) # make the MathFunctions library optional option(USE_MYMATH "Use tutorial provided math implementation" ON) # copy a file to another location and modify its contents # https://cmake.org/cmake/help/latest/command/configure_file.html configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) if(USE_MYMATH) add_subdirectory(MathFunctions) list(APPEND EXTRA_LIBS MathFunctions) list(APPEND EXTRA_INCLUDES "${PROJECT_BINARY_DIR}/MathFunctions") endif() # add the executable add_executable(CalculateSqrt calculatesqrt.cpp) # specify libraries of flags to use when linking a given target and/or its dependents # https://cmake.org/cmake/help/latest/command/target_link_libraries.html target_link_libraries(CalculateSqrt PUBLIC ${EXTRA_LIBS}) # add include directories to a target target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES})
-
再回到build目录下执行cmake即可。
2.5 安装与测试
为了把撰写的程序安装到指明的路径中,例如安装到"/usr/local/bin/"目录下,即可以在任意路径下输入脚本文件名执行命令,不需要指明脚本的路径(当然,你可以通过修改bash的环境变量也能实现相同的功能),可以使用make的安装命令。这个实现需要分别在两个CMakeLists.txt文件后面指明工程生成的目的地。
-
修改"./MathFunctions/CMakeLists.txt",指明目的地:
# ./MathFunctions/CMakeLists.txt add_library(MathFunctions mysqrt.cpp) target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) install(TARGETS MathFunctions DESTINATION lib) install(FILES MathFunctions.h DESTINATION include)
-
修改"./CMakeLists.txt",指明目的地:
# ./CMakeLists.txt # . # . # . # add include directories to a target target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES}) # add install rule install(TARGETS CalculateSqrt DESTINATION bin) install(FILES "${PROJECT_BINARY_DIR}/CalculateSqrtConfig.h" DESTINATION include )
-
回到build目录下,依次执行build, make, make install。最后make install输出的结果依次为:
以上即实现了安装的功能。为了测试编写文件是否能够运行成功,可以在CMakeLists.txt中指明测试的场景。测试这一部分只需要在"./CMakeLists.txt"添加测试命令即可。
# ./CMakeLists.txt
# .
# .
# .
# enable testing
enable_testing()
# does the application run
add_test(NAME Runs COMMAND CalculateSqrt 25)
# does the usage message work?
add_test(NAME Usage COMMAND CalculateSqrt)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# define a function to simplify adding tests
# do_test: funciton name, target: test command(CalculateSqrt)
# arg: input args, result: error message
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 a bunch of result based tests
do_test(CalculateSqrt 4 "4 is 2")
do_test(CalculateSqrt 9 "9 is 3")
do_test(CalculateSqrt 5 "5 is 2.236")
do_test(CalculateSqrt 7 "7 is 2.645")
do_test(CalculateSqrt 25 "25 is 5")
do_test(CalculateSqrt -25 "-25 is [-nan|nan|0]")
do_test(CalculateSqrt 0.0001 "0.0001 is 0.01")
然后重新构建项目,执行make生成二进制文件。然后使用ctest命令进行测试:
这里一共添加了9个测试示例,前两个测试的命令分别为“CalculateSqrt 25”与"CalculateSqrt"。当测试示例的返回值为0时,该示例通过。所以这9个示例中,只有第二个示例是没有通过的。
注意:为了完成后续实验,要记得把"/usr/local/include/"的“CalculateSqrtConfig.h”与“/usr/local/bin/”的"CalculateSqrt"给删除,如果不删除的话,后续会一直使用这两个文件。
2.6 系统自检
本实验是为了测试系统中是否自带某个函数,如本例实验中测试“math.h”中是否具有"log"与"exp"函数。这需要修改“./CMakeLists.txt”文件,和头文件的配置文件"CalculateSqrtConfig.h.in",以及自己定义的"mysqrt.cpp"。
-
在“./CMakeLists.txt”添加检查语句:
# ./CMakeLists.txt # adding system introspection include(CheckSymbolExists) set(CMAKE_REQUIRED_LIBRARIES "m") # HAVE_LOG = 1 if log in file "math.h" check_symbol_exists(log "math.h" HAVE_LOG) check_symbol_exists(exp "math.h" HAVE_EXP)
-
在"./CalculateSqrtConfig.h.in"添加包含语句:
// ./CalculateSqrtConfig.h.in // does the platform provide exp and log functions? #cmakedefine HAVE_LOG #cmakedefine HAVE_EXP
-
在"./MathFunctions/mysqrt.cpp"包含生成的头文件"CalculateSqrtConfig.h":
// ./MathFunctions/mysqrt.cpp #include "CalculateSqrtConfig.h"
然后重新cmake,make以及执行,发现平台并没有包含log与exp函数。虽然它们确实存在于"math.h"文件中。查看头文件"CalculateSqrtConfig.h",发现确实没有定义:
// ./build/CalculateSqrtConfig.h
#define USE_MYMATH
// does the platform provide exp and log functions?
/* #undef HAVE_LOG */
/* #undef HAVE_EXP */
Plus:如果不想在"./MathFunctions/mysqrt.cpp"包含生成的头文件"CalculateSqrtConfig.h",可以在“./CMakeLists.txt”添加以下语句,将定义的宏"HAVE_LOG"和"HAVE_EXP"直接生成到目标"MathFunctions"中。记住,这一段要加在"add_subdirectory(MathFunctions)"语句之后,否则cmake是看不到"MathFunctions"这个项目的:
# ./CMakeLists.txt
if(HAVE_LOG AND HAVE_EXP)
target_compile_definitions(MathFunctions
PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()
2.7 增加用户命令并产生文件
这个实验中,添加一个"./MathFunctions/MakeTable.cpp"文件,用于生成一个记录19的平方根的表格。当输入的数位于19时,会根据该表格计算平方根,否则调用sqrt函数。
在本实验开始之前,一样需要先删除"./build"目录下的所有文件,可以在"./MathFunctions/CMakeLists.txt"与"./MathFunctions/mysqrt.cpp"中删除检查"log"与"exp"符号的命令。
-
创建"./MathFunctions/MakeTable.cpp"文件,其内容如下:
// ./MathFunctions/MakeTable.cpp // A simple program that builds a sqrt table #include <stdio.h> #include <math.h> int main (int argc, char *argv[]) { int i; double result; // make sure we have enough arguments if (argc < 2) { return 1; } // open the output file FILE *fout = fopen(argv[1],"w"); if (!fout) { return 1; } // create a source file with a table of square roots fprintf(fout,"double sqrtTable[] = {\n"); for (i = 0; i < 10; ++i) { result = sqrt(static_cast<double>(i)); fprintf(fout,"%g,\n",result); } // close the table with a zero fprintf(fout,"0};\n"); fclose(fout); return 0; }
-
在"./MathFunctions/CMakeLists.txt"中添加生成可执行文件"MakeTable":
# ./MathFunctions/CMakeLists.txt # add MakeTable as another executable add_executable(MakeTable MakeTable.cxx)
-
在"./MathFunctions/CMakeLists.txt"中添加通过执行"MakeTable"生成“Table.h”的命令:
# ./MathFunctions/CMakeLists.txt # add custom command to produce Table.h add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h DEPENDS MakeTable)
-
为了告诉CMake,"./MathFunctions/mysqrt.cpp"依赖生成的"Table.h"文件,需要"./MathFunctions/CMakeLists.txt"添加library:
# ./MathFunctions/CMakeLists.txt # add library path add_library(MathFunctions mysqrt.cpp ${CMAKE_CURRENT_BINARY_DIR}/Table.h)
-
为了让"./MathFunctions/mysqrt.cpp"能看到生成的"Table.h"文件,需要把当前二进制路径加入到include目录中:
# ./MathFunctions/CMakeLists.txt target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-
修改"./calculatesqrt.cpp"文件,使用生成的"sqrtTable"表格:
// ./calculatesqrt.cpp int main(int argc, char* argv[]){ if(argc<2){ std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." << CalculateSqrt_VERSION_MINOR << std::endl; fprintf(stdout, "Uage: %s number\n", argv[0]); return 1; } double inputValue = atof(argv[1]); #ifdef USE_MYMATH double outputValue = mysqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << " with mysqrt function" << std::endl; #else double outputValue = sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; #endif return 0; }
-
回到build目录下,执行cmake,make即可。最终得到的文件目录如下:
2.8 构建安装程序
当需要把项目打包分享给其他人时,可以同时提供不同平台的二进制文件与源文件。这与2.5的构建是不同的,在2.5中,我们是将可执行文件安装到指定路径,并将生成的头文件复制到指定路径;而在这个实验中,则是构建支持二进制安装与包管理的安装包(installation packages that support binary installations and package management features)。为了实现这一点,需要使用CPack。
-
使用CPack只需要在"./CMakeLists.txt"文件的底部加上:
# ./CMakeLists.txt" # include any runtime libraries that are needed by the project for the current platform include(InstallRequiredSystemLibraries) # set some CPack variables to where we have stored the license and version information for this project. set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${CalculateSqrt_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${CalculateSqrt_VERSION_MINOR}") include(CPack)
-
执行cmake,然后执行cpack构建二进制版本,生成的结果如下:
红色方块的文件代表执行cpack打包生成的文件。
2.9 添加可视化仪表
本实验将测试用例的结果用可视化仪表来展示。
-
修改"./CMakeLists.txt"中的"enable_testing()“为"include(CTest)”:
# ./CMakeLists.txt" # enable testing # enable_testing() # enable dashboard scripting include(CTest)
-
创建"./CTestConfig.cmake"文件:
# ./CTestConfig.cmake set(CTEST_PROJECT_NAME "CalculateSqrt") 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)
这里官网提供了dash的脚本。
-
执行build,然后执行以下命令:
ctest [-VV] -C Debug -D Experimental
可以得到结果如下:
转到官网提供的网站可以看到:
2.10 构建共享库
本实验中,主要介绍使用CMake生成共享链接库。这主要通过设置camke的变量"BUILD_SHARED_LIBS"来设置库的类型。
-
修改"./CMakeLists.txt"文件,添加"BUILD_SHARED_LIBS"变量:
# ./CMakeLists.txt # ... 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) # ...
-
因为"./mysqrt.cpp"中经常使用到"MathFunctions",所以可以将其生成一个静态库"SqrtLibrary",修改"./MathFunctions/CMakeLists.txt":
# ./MathFunctions/CMakeLists.txt # add the library that runs add_library(MathFunctions mysqrt.cpp) # 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.cpp) # 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.cpp ${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} ) # 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) endif() # define the symbol stating we are using the declspec(dllexport) when # building on windows target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH") # install rules install(TARGETS MathFunctions DESTINATION lib) install(FILES MathFunctions.h DESTINATION include)
-
更新"./MathFunctions/mysqrt.cpp"文件,使用"mathfunctions"与"deltail"命名空间:
// ./MathFunctions/mysqrt.cpp #include <iostream> #include <cmath> #include "MathFunctions.h" // include the generated table #include "Table.h" namespace mathfunctions { namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { if (x <= 0) { return 0; } // use the table to help find an initial value double result = x; if (x >= 1 && x < 10) { std::cout << "Use the table to help find an initial value " << std::endl; result = sqrtTable[static_cast<int>(x)]; }else{ return sqrt(x); } // do ten iterations 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; } } }
-
修改"./calculatesqrt.cpp"文件,使其包含"MathFunctions.h"并使用"mathfunctions::mysqrt()":
// ./calculatesqrt.cpp #include "MathFunctions.h" int main(int argc, char* argv[]){ if(argc<2){ std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." << CalculateSqrt_VERSION_MINOR << std::endl; fprintf(stdout, "Uage: %s number\n", argv[0]); return 1; } double inputValue = atof(argv[1]); double outputValue = mathfunctions::mysqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << " with mysqrt function" << std::endl; return 0; }
-
最终更新"./MathFunctions/MathFunctions.h"文件,定义DECLSPEC:
// ./MathFunctions/MathFunctions.h #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); }
总结(Conclusion)
本文是关于CMake教程的一个学习笔记。较大的项目往往包含众多模块,如果要自己写makefile,那么各个模块之间的编译与包含关系足够让我们头疼的。所以借助CMake较为简单的写法,可以帮助我们生成跨平台的makefile文件,使得工程能够在不同的平台上编译。笔者对于cmake认知尚浅,所以文中可能有所纰漏,望体谅。
参考资料(Reference)
“轻松搞定CMake”系列之CMakeLists文件编写语法规则详解
`