文|Seraph
01 | CMake简介
makefile通常依赖于当前的编译平台,编写makefile的工作量很大,解决依赖关系时也容易出错。
CMake是makefile的构建工具,可以生产可移植的makefile。
使用CMake可以减少手写makefile的工作量。
02 | CMake安装
- 从CMake官网下载最新版。如下载很慢,可以使用链接https://cmake.org/files/。
- 安装跟普通程序一致,我这里使用的Mac。
- 安装好后,还需要配置环境变量,使Teminal能直接使用。
打开Tools->How to Install For Command Line Use
菜单,弹出的对话框告诉我们怎么配置。
这里显示三种方法:
第一种:临时给PATH
添加环境变量,但是重新打开Teminal后,会失效。
第二种:在/usr/local/bin
建立相应的命令链接,而/usr/local/bin
存在于系统环境变量中,所以Teminal也就能直接使用。
第三种:指定一个安装目录,建立相应的命令链接。
如上所示,我们这里使用第二种即可。 - 使用
cmake --version
查询是否配置好cmake的环境变量以及cmake的版本。
03 | CMake的“Hello World”示例
- 新建一个
Hello
目录,并在目录下添加main.cpp
,文件内容如下:
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello World"<<endl;
return 0;
}
- 再添加一个CMakeLists.txt文件,内容如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.0) #表示需要CMake最小版本
PROJECT(HELLO) #项目名称
AUX_SOURCE_DIRECTORY(. SRC_LIST) #把当前目录下所有源代码文件名加入变量SRC_LIST
ADD_EXECUTABLE(hello ${SRC_LIST}) #生成应用程序 hello
- 在
hello
目录下新建一个build
目录,然后使用CMake界面程序配置源码路径和生成二进制路径,然后点击Configure
即可。
所有编译结果文件都会输出至build
文件夹下,不会源文件混淆,容易管理。 - 使用Teminal进入
build
目录,然后分别输出cmake ..
和make
。最后运行./hello
文件,即能输出Hello World
。(这里cmake ..
等同于3的界面操作)
如果源码编写错误,则重新使用Vim编写源码,然后直接再次运行make
即可,不同重复前面的步骤。
04 | 链接库的依赖处理
- 在
hello
目录下,新建一个print
目录,并在print
目录下添加print.h
、print.cpp
,文件内容如下:
- print.h文件
#ifndef __HELLO_H__
#define __HELLO_H__
#include <iostream>
using namespace std;
void print();
#endif
- print.cpp文件
#include "print.h"
void print()
{
cout<<"Hello World!!!!!"<<endl;
}
- 修改
main.cpp
,如下:
#include "print/print.h"
int main()
{
print();//调用print中的print函数
return 0;
}
- 修改
hello
目录下的CMakelists.txt
如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
PROJECT(HELLO) #项目名称
ADD_SUBDIRECTORY(print) #添加子目录
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(hello ${SRC_LIST})
TARGET_LINK_LIBRARIES(hello Print) #链接的库
- 在
print
目录下也要添加一个CMakelists.txt
,如下:
AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)
ADD_LIBRARY ( Print ${DIR_TEST1_SRCS}) #这里的Print与上个文件里链接的库一致
当使用ADD_SUBDIRECTORY
添加一个子目录时,Configure
会去寻找该目录下的CMakelists.txt
,如果我们这里把它删了,就会报错。
- 重新
cmake ..
和make
以后,就能生成新的hello执行文件了。
05 | CMake调用其它的库(以OpenCV库为例)
- 首先安装OpenCV库。
- 新建一个
openimage
的工程目录,在openimage
下新建bin
、build
、src
目录。并新建CMakelists.txt
文件如下:
PROJECT(openimage)
cmake_minimum_required(VERSION 3.0)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${openimage_SOURCE_DIR}/bin) #设置运行输出文件目录
find_package(OpenCV REQUIRED) #查找OpenCV库
include_directories(${OpenCV_INCLUDE_DIRS}) #包括头文件,这里的值,是通过上一条find_package找到的
add_executable(openimage src/openimage.cpp) #执行文件
link_directories(${OpenCV_LIBRARY_DIRS}) #包含库
target_link_libraries(openimage ${OpenCV_LIBS}) #链接库
- 在
src
目录下新建openimage.cpp
文件,内容如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("1.png");
imshow("Viewer", img);
waitKey(0);
return 0;
}
- 在
bin
和build
目录下加入1.png
图片文件。 - 然后进入
build
目录下进行如前面介绍的一样进行cmake ..
和make
。
最后执行输出文件,便能调用OpenCV打开图片。(注意我们代码里面写的是相对路径,所以当我们执行文件时,是在命令执行目录下面寻找图片文件,而不是执行文件openimage目录下)
06 | 生成debug版和release版执行程序
- C++之所以会生成不同版本的程序,是因为g++的参数不一样,
-O3
表示优化程度调到最高,-O0
表示优化程度调到最低,-g -ggdb
表示添加调试信息。 - CMake中有一个变量
CMAKE_BUILD_TPYE
,当其取值为Debug
时,会使用CMAKE_CXX_FLAGS_DEBUG
选项生成makefile;当其取值为Release
时,会使用CMAKE_CXX_FLAGS_RELEASE
选项来生成makefile。 - 我们可以通过给如上两个选项赋相应的值来设置
Debug
编译和Release
编译。我们这里继续采用hello项目继续试验,修改CMakeLists.txt
如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
PROJECT(HELLO) #项目名称
ADD_SUBDIRECTORY(print)
AUX_SOURCE_DIRECTORY(. SRC_LIST)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
ADD_EXECUTABLE(hello ${SRC_LIST})
TARGET_LINK_LIBRARIES(hello Print)
- 进入
build
目录,使用ccmake ..
进行configure
和generate
。在执行之前,修改CMAKE_BUILD_TPYE
的值为Debug
或Release
,便能生成对应的程序版本。
具体快捷键操作,键ccmake
下端提示。因为hello程序很小,所以看不出来debug版本release版有什么区别。
07 | 《cmake实践》总结
推荐去看《cmake实践》这篇文章,大概50页,入门很详细,解析很清楚。
make -VERBOSE=1
可显示详细构建过程。make clean
清理构建工程。- 为了文件不混淆,一般使用外部编译。
ADD_LIBRARY
不能同时生成同名的目标文件(动态库、静态库),需要使用SET_TARGET_PROPERTIES
修改输出名。
文章的一些错误和注意事项:
- P6页中CMakeLists.txt内容的最后一样应为:
ADD_EXECUTABLE(hello ${SRC_LIST})
这里需要用${}
的形式获取源文件列表。 - P19页中未给执行程序
hello
添加安装信息。正确的做法是在src
目录下的CMakeLists.txt
文件末尾添加如下代码:
INSTALL(TARGETS hello RUNTIME DESTINATION bin)
不要添加到工程根目录下的CMakeLists.txt
中,因为hello
编译是由src
下的CMakeLists.txt
执行的。
- P25页的
make install
在mac下,如果加sudo
还是执行安装失败。可能是mac处于某种保护机制,需要重启进入恢复模式,关闭该保护机制。见《MAC使用》中问题解决的第1个问题。 - P27页缺少
LINK_DIRECTORIES(/usr/lib)
,但是我加上了也依然连接不上。查了下,新版不推荐使用LINK_DIRECTORIES
,可以使用FIND_LIBRARY
代替。所有将src
下的CMakeLists.txt
文件内容修改为:
ADD_EXECUTABLE(main main.c)
INCLUDE_DIRECTORIES(/usr/include/hello)
FIND_LIBRARY(LIBHELLO_PATH hello /usr/lib)
IF(NOT LIBHELLO_PATH)
MESSAGE(FATAL_ERROR "libhello not found")
ENDIF(NOT LIBHELLO_PATH)
TARGET_LINK_LIBRARIES(main ${LIBHELLO_PATH})
问题解决:
CMake Error at CMakeLists.txt:7 (add_executable):
The target name “test” is reserved or not valid for certain CMake features, such as generator expressions, and may result in undefined behavior.
- cmake保留test,将工程名修改为其它即可。