利用cmake管理工程

近期由于做一个项目,需要重写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 main
	PRE_LINK
	WORKING_DIRECTORY ${BINARY_ROOT_DIR}/lib
	link_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]$ make
Scanning dependencies of target hello
[ 50%] Building C object lib/CMakeFiles/hello.dir/hello.c.o
Linking C static library libhello.a
[ 50%] Built target hello
Scanning dependencies of target main
[100%] Building C object main/CMakeFiles/main.dir/main.c.o
Linking C executable main
[100%] Built target main
完成编译。

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

3. cmake中需要注意的地方

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

1.如何加入交叉编译

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

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

# specify the cross compiler
set(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 directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
	set(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的效果图如下所示

利用cmake管理工程_第1张图片


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在编译流程的各个阶段分别做了哪些事儿,上手速度会更快。


你可能感兴趣的:(Build,Path,library,compiler,makefile,dependencies)