您的位置:首页 > 其它

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 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)                           ====》 链接自己实现的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.000000
   helloworld会链接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
看来我们的程序是可靠的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: