cmake 从入门到精通(一)
2013-12-25 23:03
323 查看
引入
开源数据库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.c 1 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 check project(helloworld) --set project name aux_source_directory("${PROJECT_SOURCE_DIR}/src" src) -- get all source files under src and set it to variable src add_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.c 2 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]$ make Scanning dependencies of target helloworld [100%] Building C object CMakeFiles/helloworld.dir/src/main.c.o Linking 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-list cmake version 2.8.12.1 add_compile_options add_custom_command add_custom_target add_definitions add_dependencies add_executable add_library add_subdirectory add_test aux_source_directory .....
[billi@localhost build]$ cmake --help-command project cmake 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.1 CMAKE_AR CMAKE_ARGC CMAKE_ARGV0 CMAKE_BINARY_DIR CMAKE_BUILD_TOOL CMAKE_CACHEFILE_DIR CMAKE_CACHE_MAJOR_VERSION CMAKE_CACHE_MINOR_VERSION CMAKE_CACHE_PATCH_VERSION CMAKE_CFG_INTDIR CMAKE_COMMAND CMAKE_CROSSCOMPILING CMAKE_CTEST_COMMAND CMAKE_CURRENT_BINARY_DIR CMAKE_CURRENT_LIST_DIR CMAKE_CURRENT_LIST_FILE CMAKE_CURRENT_LIST_LINE CMAKE_CURRENT_SOURCE_DIR CMAKE_DL_LIBS CMAKE_EDIT_COMMAND CMAKE_EXECUTABLE_SUFFIX CMAKE_EXTRA_GENERATOR CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES CMAKE_GENERATOR CMAKE_GENERATOR_TOOLSET CMAKE_HOME_DIRECTORY CMAKE_IMPORT_LIBRARY_PREFIX ....
[billi@localhost build]$ cmake --help-variable-list | grep -i project CMAKE_PROJECT_NAME PROJECT_BINARY_DIR PROJECT_NAME PROJECT_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.c dir = /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_H float 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.h 2 directories, 4 files但是make的时候出错,错误居然是头文件找不到,不是在同一个目录吗,看来cmake也不是那么智能。
[billi@localhost build]$ make Scanning 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 1 make[1]: *** [CMakeFiles/helloworld.dir/all] Error 2 make: *** [all] Error 2应该有添加头文件的命令, cmake --help-command-list | grep include
[billi@localhost helloworld]$ cmake --help-command-list | grep include include include_directories include_external_msproject include_regular_expression target_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 libraryadd_libraryfind_libraryexport_library_dependencies[billi@localhost tutor]$ cmake --help-command-list | grep librariestarget_link_librarieslink_libraries应该使用link_libraries,添加link_libraries(m), 它的作用域比target_link_libraries要大。
[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") link_libraries(m) add_executable(helloworld ${src}) message("src = " ${src}) message("dir = " ${helloworld_SOURCE_DIR})最后结果[billi@localhost build]$ ./helloworldHello, 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.h 4 directories, 7 filesmysqrt.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) ====》 链接自己实现的MathFunctions add_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 directory src = /home/billi/projects/cmake_learn/tests/helloworld/src/MathFunctions/src/mysqrt.c src = /home/billi/projects/cmake_learn/tests/helloworld/src/sqrt.c/home/billi/projects/cmake_learn/tests/helloworld/src/main.c dir = /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 helloworld Hello, World! 10.000000 Please 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 library add_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 library add_library(MathFunctions SHARED ${math_src}) message("src = " ${math_src})构建结果为:
[billi@localhost build]$ cmake ..; make;./helloworldsrc = /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.000000helloworld会链接MathFunctions.so, 当同时为静态链接库跟动态链接库时会优先选择动态链接库。
[billi@localhost build]$ ldd helloworldlinux-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.txtcmake_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/buildStart 1: first1/2 Test #1: first ............................ Passed 0.00 secStart 2: second2/2 Test #2: second ........................... Passed 0.00 sec100% tests passed, 0 tests failed out of 2Total Test time (real) = 0.01 sec看来我们的程序是可靠的。
相关文章推荐
- iOS开发-UI 从入门到精通(二)
- Python 3从入门到精通13-读文件内容
- 易信公众平台开发从入门到精通之开发验证
- Nginx 开发从入门到精通
- 【备忘】年薪50万2017年最新北风网Spark2.0从入门到精通教程
- MyBatis 入门到精通(一) 了解MyBatis获取SqlSession .
- Oracle 入门到精通Part 1-用户管理
- Charles从入门到精通
- STM32从入门到精通
- Java学习从入门到精通
- 蓝鸥iOS从零基础到精通就业-OC语言入门 1和对象一1
- GN3 从入门到精通视频教程
- 软件测试自学指南---从入门到精通
- Charles 从入门到精通
- Java入门到精通——工具篇之Maven概述
- 正则表达式从入门到精通
- 汇编学习从入门到精通 step by step
- Linux编程从入门到精通
- mybatis 从入门到精通(三)