利用cmake管理工程

来源:互联网 发布:java list《map 编辑:程序博客网 时间:2024/04/29 23:37

近期由于做一个项目,需要重写Makefile,开始打算用GUN的aotu tools,但是考虑到上手不易,而且用起来复杂,最后用了cmake替换。情况还不错。自己也顺便总结了cmake的一些常用方法及注意事项。我特意写了一个最小化的项目说明cmake的注意事项。


1.源代码

首先建立项目工程目录prg,目录及结构如下:

[ycwang@ycwang:prg]$ tree
.
├── build
├── CMakeLists.txt
├── hello.h
├── lib
│   ├── CMakeLists.txt
│   └── hello.c
└── main
    ├── CMakeLists.txt
    └── main.c

项目中包括三个目录,build目录用于实现外部编译,用于存放编译中产生的文件,lib目录用于生成库,main目录用于其它源码。


这几个文件的内容如下:

1. prg/CMakeLists.txt

project(main)cmake_minimum_required(VERSION 2.8)include_directories(.)set(lib_src)add_definitions(-static)add_subdirectory(lib)add_subdirectory(main)

2. prg/hello.h

int hello(void);

3. prg/lib/hello.c

#include <stdio.h>#include <hello.h>int hello(){printf("*****************************\n");printf("Hello world\n");printf("*****************************\n");return 0;}

4. prg/lib/CMakeLists.txt

set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)set(hello_src hello.c)add_library(hello STATIC ${hello_src})#add_library(hello SHARED ${hello_src})

5. prg/main/main.c

#include <stdio.h>#include <hello.h>int main(){printf("Using lib hello\n");hello();printf("filnished\n");return 0;}

6. prg/main/CMakeLists.txt

set(main_src main.c)list(APPEND lib_src hello)add_custom_command(TARGET mainPRE_LINKWORKING_DIRECTORY ${BINARY_ROOT_DIR}/liblink_directories(${BINARY_ROOT_DIR}/lib))add_executable(main ${main_src})#target_link_libraries(main "hello")target_link_libraries(main ${lib_src})


2. 编译源代码

进入prg/build目录

[ycwang@ycwang:lib]$ cd build/[ycwang@ycwang:build]$ cmake ..

会输出以下内容:

[ycwang@ycwang:build]$ cmake ../-- The C compiler identification is GNU-- The CXX compiler identification is GNU-- Check for working C compiler: /usr/bin/gcc-- Check for working C compiler: /usr/bin/gcc -- 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/ycwang/desktop/myself/c-program/cmake/lib/build
这时已经生成了项目的Makefile

[ycwang@ycwang:build]$ makeScanning dependencies of target hello[ 50%] Building C object lib/CMakeFiles/hello.dir/hello.c.oLinking C static library libhello.a[ 50%] Built target helloScanning dependencies of target main[100%] Building C object main/CMakeFiles/main.dir/main.c.oLinking C executable main[100%] Built target main
完成编译。

可见CMake的编译及生成特别简单。

3. cmake中需要注意的地方

由于cmake是在传统的GNU的Makefile之上抽象出的一层,封装了Makefile的一些命令。还是需要值得注意一些差别的。

1.如何加入交叉编译

由于嵌入式平台使用交叉编译工具链比较频繁。用cmake如何使用这些工具呢?

编写一个cmake的脚本Toolchain-mips.cmake,放在prg目录下。

# specify the cross compilerset(DIRECTORY /opt/mipseltools-gcc412-glibc261/bin/)set(PREF ${DIRECTORY}mipsel-linux-)set(CMAKE_C_COMPILER   ${PREF}gcc)set(CMAKE_CXX_COMPILER ${PREF}g++)#message(STATUS ${CMAKE_C_COMPILER})# where is the target environment set(CMAKE_FIND_ROOT_PATH  /opt/mipseltools-gcc412-glibc261/bin/)# search for programs in the build host directoriesset(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)# for libraries and headers in the target directoriesset(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
然后在运行cmake 时

cmake -DCMAKE_TOOLCHAIN_FILE=../Toolchain-mips.cmake ../
其中的-DCMAKE_TOOLCHAIN_FILE参数是必须的,如果您硬是要把这些命令直接写在CMakeFiles.txt中,那么运行cmake时,它会先去寻找系统中的工具链,然后再读取CMakeFiles.txt中的内容。无法配置成交叉编译的环境。


2. 利用cmake生成源代码的流程图

[ycwang@ycwang:build]$ cmake ../ --graphviz=test.dot
会在配置完之后产生test.dot文件,利用dot命令转换

[ycwang@ycwang:build]$ dot -Tjpg test.dot -o test.jpg
生成的test.jpg的效果图如下所示



3. 动态库与静态库的问题

在prg/lib/CMakeLists.txt的代码中有这么一行,

add_library(hello STATIC ${hello_src})
第一个参数可以是STATIC或者是SHARED,对应着最后的target_link_library中是静态库或者是动态库生成最终的可执行文件。如果没有生成静态库,那么传递给编译器的参数-static将不会起作用。


4. 项目中包括可裁剪的库的问题

如果项目本身是以模块形式生成出来的,而且可以利用宏开关动态的裁剪编译的模块。也就意味着生成的动态或者静态库在变化着。可以在判断编译这个模块的代码中加入

list(APPEND virable modname),最后把virable这个变量传到target_link_library中。最后会按照裁剪的结果生成最终的程序。

如果你的项目中有库,那么需要在最后生成可执行文件时保证先把库生成出来,再做最后的链接,可以用add_custom_command函数,使用方法如prg/main/CMakeLists.txt所示。


4.总结

总的来说,cmake上手比较快,而且使用方使,适合管理大型的项目,但是cmake本身是一门语言,要用精通需要了解很多内容。如果你对Make的基本流程比较熟,知道一般的编译流程,然后再去理解cmake在编译流程的各个阶段分别做了哪些事儿,上手速度会更快。