cmake 从入门到精通(一)

来源:互联网 发布:手机晒密软件 编辑:程序博客网 时间:2024/04/24 08:57

引入

  • 开源数据库MySQL   用cmake构建
  • 开源数据库MariaDB用cmake构建
  • 越来越多的系统使用cmake....
  • 或许不久将来Nginx也会使用cmake
  如果阅读MySQL源代码,想动手修改代码编译测试,不懂cmake那绝对不行,本文剖析如何用cmake来构建C项目代码,一步一步走进cmake的神奇世界....

目录


例子

学习内容

单个源文件main.c

多个源代码与链接库

多个相互依赖模块

使用静态库与动态库

添加测试用例

程序与库生成与安装


例子一


       经典的hello, world! 如何使用cmake构建呢?

[billi@localhost tests]$ tree helloworld/helloworld/`-- src    `-- main.c1 directory, 1 file

[billi@localhost helloworld]$ cat src/main.c #include <stdio.h>#include <stdlib.h>int main(int argc, char* argv[]){rintf("Hello, World!\n");return 0;}

cmake以CMakeLists.txt为驱动,首先在helloworld项目目录内编写总工程CMakeLists.txt

[billi@localhost helloworld]$ cat CMakeLists.txt   cmake_minimum_required(VERSION 2.8)     --minimum cmake version checkproject(helloworld)                     --set project nameaux_source_directory("${PROJECT_SOURCE_DIR}/src" src) -- get all source files under src and set it to variable srcadd_executable(helloworld ${src})                     -- add target helloworld which comes from all source files

为了不让cmake产生的一些文件以及生成的程序与库不与源代码结构混在一起,我们采用out-of-source的方式构建,方式就是新建一个build目录到工程目录下,目录结构如下:
[billi@localhost helloworld]$ tree.|-- build|-- CMakeLists.txt`-- src    `-- main.c2 directories, 2 files
转到build目录执行如下(注: cmake 只认CMakeLists.txt,所以才使用cmake 上级目录),
[billi@localhost build]$ cmake .. -- The C compiler identification is GNU 4.8.2-- The CXX compiler identification is GNU 4.8.2-- Check for working C compiler: /usr/bin/cc-- Check for working C compiler: /usr/bin/cc -- works-- Detecting C compiler ABI info-- Detecting C compiler ABI info - done-- Check for working CXX compiler: /usr/bin/c++-- Check for working CXX compiler: /usr/bin/c++ -- works-- Detecting CXX compiler ABI info-- Detecting CXX compiler ABI info - done-- Configuring done-- Generating done-- Build files have been written to: /home/billi/projects/cmake_learn/tests/helloworld/build[billi@localhost build]$ makeScanning dependencies of target helloworld[100%] Building C object CMakeFiles/helloworld.dir/src/main.c.oLinking C executable helloworld[100%] Built target helloworld

到这里,helloworld工程构建成功,执行它就会输出熟悉hello, world! 
我们可以感受到cmake比autoconf, automake简单多了,但是不熟悉cmake我们还是会使用autoconf, automake, 毕竟cmake熟悉需要一个过程。

cmake


       CMAKE如此强大,如何掌握它?
   执行cmake会输出Usage, 这里我们简单来学习cmake的命令与变量,上面的CMakeLists.txt使用了project以及aux_source_directory命令,那到底cmake支持那些命令呢?如下所示:
   
[billi@localhost build]$ cmake --help-command-listcmake version 2.8.12.1add_compile_optionsadd_custom_commandadd_custom_targetadd_definitionsadd_dependenciesadd_executableadd_libraryadd_subdirectoryadd_testaux_source_directory.....

[billi@localhost build]$ cmake --help-command projectcmake version 2.8.12.1  project       Set a name for the entire project.         project(<projectname> [languageName1 languageName2 ... ] )       Sets the name of the project.  Additionally this sets the variables       <projectName>_BINARY_DIR and <projectName>_SOURCE_DIR to the       respective values.       Optionally you can specify which languages your project supports.       Example languages are CXX (i.e.  C++), C, Fortran, etc.  By default C       and CXX are enabled.  E.g.  if you do not have a C++ compiler, you can       disable the check for it by explicitly listing the languages you want       to support, e.g.  C.  By using the special language "NONE" all checks       for any language can be disabled.  If a variable exists called       CMAKE_PROJECT_<projectName>_INCLUDE, the file pointed to by that       variable will be included as the last step of the project command.       The top-level CMakeLists.txt file for a project must contain a       literal, direct call to the project() command; loading one through the       include() command is not sufficient.  If no such call exists CMake       will implicitly add one to the top that enables the default languages       (C and CXX).

据上知,我们可以查询所用的cmake命令的基本用法;下面我们介绍变量的查询,上面的CMakeLists.txt使用了src与PROJECT_SOURCE_DIR等变量,他们应该是全局变量,CMakeLists.txt在工程顶层,PROJECT_SOURCE_DIR属于cmake自己预置的全局变量。

[billi@localhost build]$ cmake --help-variable-list cmake version 2.8.12.1CMAKE_ARCMAKE_ARGCCMAKE_ARGV0CMAKE_BINARY_DIRCMAKE_BUILD_TOOLCMAKE_CACHEFILE_DIRCMAKE_CACHE_MAJOR_VERSIONCMAKE_CACHE_MINOR_VERSIONCMAKE_CACHE_PATCH_VERSIONCMAKE_CFG_INTDIRCMAKE_COMMANDCMAKE_CROSSCOMPILINGCMAKE_CTEST_COMMANDCMAKE_CURRENT_BINARY_DIRCMAKE_CURRENT_LIST_DIRCMAKE_CURRENT_LIST_FILECMAKE_CURRENT_LIST_LINECMAKE_CURRENT_SOURCE_DIRCMAKE_DL_LIBSCMAKE_EDIT_COMMANDCMAKE_EXECUTABLE_SUFFIXCMAKE_EXTRA_GENERATORCMAKE_EXTRA_SHARED_LIBRARY_SUFFIXESCMAKE_GENERATORCMAKE_GENERATOR_TOOLSETCMAKE_HOME_DIRECTORYCMAKE_IMPORT_LIBRARY_PREFIX....

[billi@localhost build]$ cmake --help-variable-list | grep -i projectCMAKE_PROJECT_NAMEPROJECT_BINARY_DIRPROJECT_NAMEPROJECT_SOURCE_DIR[Project name]_BINARY_DIR[Project name]_SOURCE_DIR

对的,我们确实使用了系统的全局变量,而且这里还有helloworld_SOURCE_DIR这个变量存在。
如何输出这些变量,shell 有echo, perl, python 有print, cmake有message, 可以用cmake --help-command message如查看用法。
因为我们修改CMakeLists.txt, 输出这些变量看看。

[billi@localhost build]$ cat ../CMakeLists.txt cmake_minimum_required(VERSION 2.8)project(helloworld)aux_source_directory("${PROJECT_SOURCE_DIR}/src" src)add_executable(helloworld ${src})message("src  = " ${src})message("dir  = " ${helloworld_SOURCE_DIR})

[billi@localhost build]$ cmake ..src  = /home/billi/projects/cmake_learn/tests/helloworld/src/main.cdir  = /home/billi/projects/cmake_learn/tests/helloworld-- Configuring done-- Generating done-- Build files have been written to: /home/billi/projects/cmake_learn/tests/helloworld/build

例子二


       这里只有一个文件,我们用gcc也可以编译, 这个例子我们增加一个计算平方根的模块, sqrt.h和sqrt.c

[billi@localhost src]$ cat sqrt.h #ifndef _SQRT_H#define _SQRT_Hfloat msqrt(float n);#endif

[billi@localhost src]$ cat sqrt.c#include <sqrt.h>#include <math.h>float msqrt(float n){return sqrt(n);}

[billi@localhost helloworld]$ cat src/main.c #include <stdio.h>#include <stdlib.h>#include <sqrt.h>int main(int argc, char* argv[]){printf("Hello, World!\n");printf("%f\n", msqrt(100));return 0;}


目录结构如下,执行过程同上。

[billi@localhost helloworld]$ tree.|-- build|-- CMakeLists.txt`-- src    |-- main.c    |-- sqrt.c    `-- sqrt.h2 directories, 4 files

但是make的时候出错,错误居然是头文件找不到,不是在同一个目录吗,看来cmake也不是那么智能。

[billi@localhost build]$ makeScanning dependencies of target helloworld[ 50%] Building C object CMakeFiles/helloworld.dir/src/sqrt.c.o/home/billi/projects/cmake_learn/tests/helloworld/src/sqrt.c:1:18: fatal error: sqrt.h: No such file or directory #include <sqrt.h>                  ^compilation terminated.make[2]: *** [CMakeFiles/helloworld.dir/src/sqrt.c.o] Error 1make[1]: *** [CMakeFiles/helloworld.dir/all] Error 2make: *** [all] Error 2

应该有添加头文件的命令, cmake --help-command-list | grep include
[billi@localhost helloworld]$ cmake --help-command-list | grep includeincludeinclude_directoriesinclude_external_msprojectinclude_regular_expressiontarget_include_directories

修改CMakeLIsts.txt 为:

[billi@localhost helloworld]$ cat CMakeLists.txt cmake_minimum_required(VERSION 2.8)project(helloworld)aux_source_directory("${PROJECT_SOURCE_DIR}/src" src)include_directories("${PROJECT_SOURCE_DIR}/src")add_executable(helloworld ${src})message("src  = " ${src})message("dir  = " ${helloworld_SOURCE_DIR})

再次make,头文件找到了,但是sqrt数学函数库没有找到, 再次查找 cmake --help-command-list | grep library

[billi@localhost tutor]$ cmake --help-command-list | grep library
add_library
find_library
export_library_dependencies
[billi@localhost tutor]$ cmake --help-command-list | grep libraries
target_link_libraries
link_libraries

应该使用link_libraries,添加link_libraries(m), 它的作用域比target_link_libraries要大。
[billi@localhost helloworld]$ cat CMakeLists.txtcmake_minimum_required(VERSION 2.8)project(helloworld)aux_source_directory("${PROJECT_SOURCE_DIR}/src" src)include_directories("${PROJECT_SOURCE_DIR}/src")link_libraries(m)add_executable(helloworld ${src})message("src  = " ${src})message("dir  = " ${helloworld_SOURCE_DIR})

最后结果
[billi@localhost build]$ ./helloworld 
Hello, World!
10.000000

例子三


       老使用别人的数学函数库不行,我们得自己开发,于是我们写一个简单的计算平方根的数据库模块为MathFunctions.
       在项目src目录下新建MathFunctions目录如下:
  
[billi@localhost helloworld]$ tree.|-- build|-- CMakeLists.txt`-- src    |-- main.c    |-- MathFunctions    |   |-- CMakeLists.txt    |   `-- src    |       |-- mysqrt.c    |       `-- mysqrt.h    |-- sqrt.c    `-- sqrt.h4 directories, 7 files

         mysqrt.c 很简单,我没有实现mysqrt,不会实现,需要数学知识,所以只是简单的返回原来的。
   
[billi@localhost helloworld]$ cat src/MathFunctions/src/mysqrt.c#include <mysqrt.h>#include <float.h>#include <stdio.h>float mysqrt(float n){printf("Please implement mysqrt function...");return n;}

    helloworld/CMakeLists.txt内容为:
  
[billi@localhost helloworld]$ cat CMakeLists.txt cmake_minimum_required(VERSION 2.8)project(helloworld)add_subdirectory("${PROJECT_SOURCE_DIR}/src/MathFunctions")    ====》 添加子目录的编译依赖aux_source_directory("${PROJECT_SOURCE_DIR}/src" src)include_directories("${PROJECT_SOURCE_DIR}/src"             "${PROJECT_SOURCE_DIR}/src/MathFunctions/src"  ====》 添加子目录的头文件)link_libraries(m MathFunctions)                           ====》 链接自己实现的MathFunctionsadd_executable(helloworld ${src})message("src  = " ${src})message("dir  = " ${helloworld_SOURCE_DIR})

     编译执行为:

[billi@localhost build]$ cd build/; cmake ..; make ; ./helloworld bash: cd: build/: No such file or directorysrc  = /home/billi/projects/cmake_learn/tests/helloworld/src/MathFunctions/src/mysqrt.csrc  = /home/billi/projects/cmake_learn/tests/helloworld/src/sqrt.c/home/billi/projects/cmake_learn/tests/helloworld/src/main.cdir  = /home/billi/projects/cmake_learn/tests/helloworld-- Configuring done-- Generating done-- Build files have been written to: /home/billi/projects/cmake_learn/tests/helloworld/build[ 33%] Built target MathFunctions[100%] Built target helloworldHello, World!10.000000Please implement mysqrt function...100.000000

例子四


       在例子三中我们只生成了MathFunctions静态库,现在我们还要生成动态库,因为动态库很方便些。
  编辑MathFunctions/CMakeLists.txt为:
   
aux_source_directory("${PROJECT_SOURCE_DIR}/src/MathFunctions/src" math_src)include_directories("${PROJECT_SOURCE_DIR}/src/MathFunctions/src")### generate static link libraryadd_library(MathFunctions_static STATIC ${math_src})set_target_properties(MathFunctions_static PROPERTIES OUTPUT_NAME "MathFunctions")set_target_properties(MathFunctions_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)### generate dynamic link libraryadd_library(MathFunctions SHARED ${math_src})message("src  = " ${math_src})

       构建结果为:
 
[billi@localhost build]$ cmake ..; make;./helloworld src  = /home/billi/projects/cmake_learn/tests/helloworld/src/MathFunctions/src/mysqrt.csrc  = /home/billi/projects/cmake_learn/tests/helloworld/src/sqrt.c/home/billi/projects/cmake_learn/tests/helloworld/src/main.cdir  = /home/billi/projects/cmake_learn/tests/helloworld-- Configuring done-- Generating done-- Build files have been written to: /home/billi/projects/cmake_learn/tests/helloworld/build[ 25%] Built target MathFunctions[ 75%] Built target helloworld[100%] Built target MathFunctions_staticHello, World!10.000000Please implement mysqrt function...100.000000

   helloworld会链接MathFunctions.so, 当同时为静态链接库跟动态链接库时会优先选择动态链接库。
[billi@localhost build]$ ldd helloworld linux-vdso.so.1 =>  (0x00007fff4c1fe000)libm.so.6 => /lib64/libm.so.6 (0x000000374e600000)libMathFunctions.so => /home/billi/projects/cmake_learn/tests/helloworld/build/src/MathFunctions/libMathFunctions.so (0x00007f0d47750000)libc.so.6 => /lib64/libc.so.6 (0x000000374d600000)/lib64/ld-linux-x86-64.so.2 (0x000000374d200000)


例子五


       程序构建好了,但是我们不知道程序运行是否正确,或许运行一两次可以,但是持久运行就会出错了。
  因为我们得添加测试用例去好好测试程序得鲁棒性。
  
  ctest 是添加测试用例得工具,修改CMakeLists.txt为:
[billi@localhost build]$ cat ../CMakeLists.txt cmake_minimum_required(VERSION 2.8)project(helloworld)add_subdirectory("${PROJECT_SOURCE_DIR}/src/MathFunctions")aux_source_directory("${PROJECT_SOURCE_DIR}/src" src)include_directories("${PROJECT_SOURCE_DIR}/src"             "${PROJECT_SOURCE_DIR}/src/MathFunctions/src")link_libraries(m MathFunctions)add_executable(helloworld ${src})message("src  = " ${src})message("dir  = " ${helloworld_SOURCE_DIR})### enable ctestenable_testing()### add test case 1add_test(first helloworld)### add test case 2add_test(second helloworld)set_tests_properties(second PROPERTIES PASS_REGULAR_EXPRESSION "10*")

   执行如下命令的结果为:
 
[billi@localhost build]$ make testRunning tests...Test project /home/billi/projects/cmake_learn/tests/helloworld/build    Start 1: first1/2 Test #1: first ............................   Passed    0.00 sec    Start 2: second2/2 Test #2: second ...........................   Passed    0.00 sec100% tests passed, 0 tests failed out of 2Total Test time (real) =   0.01 sec

            看来我们的程序是可靠的。



































0 0
原创粉丝点击