1、单目录下,单个源文件或多个源文件的编译
假如某个项目的目录下只有一个源文件main.cpp或者有几个cpp文件,那么,CMakeLists应该这么写:
# 当前你的项目只有一个源文件main.cpp
# 此时只需要在main.cpp的相同目录下新建CMakeLists文件
# 规定cmake程序的最低版本
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
# 添加c++ 11标准支持
set( CMAKE_CXX_FLAGS "-std=c++11" )
# ADD_EXECUTABEL定义了这个工程会生成一个名为Main的可执行文件
# 相关的源文件是SRC_LIST中定义的源文件列表
# 方式1
set( SRC_LIST main.cpp
#子目录1/fun1.cpp
#子目录2/fun2.cpp
#...
)
# 利用${}取变量的地址,但是在IF控制语句中是直接使用变量名
# 方式1对应
ADD_EXECUTABLE( Main ${SRC_LIST} )
# 方式2
# 直接使用ADD_EXECUTABLE
#ADD_EXECUTABLE( Main main.cpp ... ... )
2、多目录下,多个源文件的编译
假如项目的tree如下
其中,main函数包含两个子目录的头文件,子目录下的源文件包含上层目录对应的头文件
可以有两种方式编写CMakeLists.txt
1)只在项目根目录下编写唯一一个CMakeLists.txt
2)每个目录下均编写一个CMakeLists.txt
# ~/workspace/CMakeLists.txt
# 规定cmake程序的最低版本
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
# ADD_SUBDIRECTORY命令用于向当前工程添加存放源文件的子目录,并且可以制定中间二进制和目标二进制存放的位置
# ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
ADD_SUBDIRECTORY( mod1 )
ADD_SUBDIRECTORY( mod2 )
# ADD_EXECUTABLE定义了这个工程会生成一个文件名为Main的可执行文件
# 相关的源文件是通过SET命令定义的参数SRC_LIST定义的源文件列表
SET( SRC_LIST main.cpp )
ADD_EXECUTABLE( Main ${SRC_LIST} )
# TARGET_LINK_LIBRARIES命令指定在链接目标文件的时候需要链接的外部库,解决外部库的依赖问题
# 它会自动寻找对应名称的库文件,而无需书写完整的路径
# 这里的mod1和mod2是在子目录的CMakeLists中用ADD_LIBRARY命令将子目录下的源文件生成的链接文件
TARGET_LINk_LIBRARIES( Main mod1 mod2 )
# ~/workspace/mod1/CMakeLists.txt
# 规定cmake程序的最低版本
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
# ADD_LIBRARY命令将指定的源文件生成链接文件
# ADD_LIBRARY( <lib_name> ------表示库文件的名字
# [STATIC | SHARED | MODULE]
# [EXCLUDE_FROM_ALL]
# [sourcefile1] [sourcefile2] [...] )
# STATIC库是目标文件的归档文件(静态库),在链接其他目标时使用
# SHARED库会被动态链接(动态链接库),在运行时会被加载
# 下面会生成mod1/libmod1.a文件
SET( MOD1_LIST mod1fun1.cpp mod1fun2.cpp )
ADD_LIBRARY( mod1 STATIC ${MOD1_LIST} )
# ~/workspace/mod2/CMakeLists.txt
# 规定cmake程序的最低版本
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
# ADD_LIBRARY命令将指定的源文件生成链接文件
# ADD_LIBRARY( <lib_name> ------表示库文件的名字
# [STATIC | SHARED | MODULE]
# [EXCLUDE_FROM_ALL]
# [sourcefile1] [sourcefile2] [...] )
# STATIC库是目标文件的归档文件(静态库),在链接其他目标时使用
# SHARED库会被动态链接(动态链接库),在运行时会被加载
# 下面会生成mod2/lib_mod2.a文件
SET( MOD2_LIST mod2fun1.cpp mod2fun2.cpp )
ADD_LIBRARY( mod2 STATIC ${MOD2_LIST} )
接下来进行外部编译以后,在build文件夹内,会生成可执行文件Main,mod1目录,mod2目录,以及其他一些中间文件
其中,mod1和mod2目录下是这样的,可以看到,生成了libxxx.a文件
假如将子目录下的CMakeLists中ADD_EXECUTABLE命令中的参数改成SHARED,那么就是这样的
可以看到,生成了libxxx.so文件
3、使用OpenCV
那么,假如我想使用OpenCV,那应该怎么做呢
因为这是一个外部库,我事先并不知道它的头文件和链接库的位置,所以要在编译命令中加上包含它们的查找路径
可以在项目目录下的CMakeLists中使用FIND_PACKAGE命令来寻找OpenCV库
并用INCLUDE_DIRECTORIES命令来添加头文件
# 寻找OpenCV库
FIND_PACKAGE( OpenCV REQUIRED )
# 添加头文件
INCLUDE_DIRECTORIES( ${OpenCV_INCLUDE_DIRS} )
可以用MESSAGE命令输出OpenCV相关信息
MESSAGE(STATUS "OpenCV library status:")
MESSAGE(STATUS " version: ${OpenCV_VERSION}")
MESSAGE(STATUS " libraries: ${OpenCV_LIBS}")
MESSAGE(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
最后,在利用ADD_EXECUTABLE命令生成可执行文件以后,将其与OpenCV库链接起来(与mod1类似)
# 链接OpenCV库
TARGET_LINK_LIBRARIES( Main ${OpenCV_LIBS} )
注意,这里是将OpenCV的库与可执行文件链接起来,而不是使用PROJECT命令指定的工程名
而PROJECT指定的是工程名,项目最终编译生成的可执行文件并不一定是这个项目名称,而是由ADD_EXECUTABLE命令确定的。
但是PROJECT指定的项目名称还是必要的,因为在cmake中有两个预定义变量:
< projectname >_BINARY_DIR 以及 < projectname >_SOURCE_DIR
内部编译情况下两者相同,而在外部编译的情况下两者所指代的内容会有所不同。
同时cmake还预定义了
PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR
在项目中,PROJECT_BINARY_DIR等同于< projectname >_BINARY_DIR,
PROJECT_SOURCE_DIR等同于< projectname >_SOURCE_DIR。
在实际的应用中,应使用PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变量,这样即使项目名称发生变化也不会影响CMakeLists.txt文件。
如此,即可在main.cpp中包含opencv的头文件,并做一个简单的矩阵显示了
一些需要注意的地方:
1)注意几个的区别
A.INCLUDE_DIRECTORIES命令添加头文件目录,比如
INCLUDE_DIRECTORIES("/opt/MATLAB/R2012a/extern/include")
B. LINK_LIBRARIES命令添加需要链接的库文件目录
相当于环境变量添加LD_LIBRARY_PATH的路径的作用,比如
#LINK_DIRECTORIES(directory1 directory2 ...)
LINK_DIRECTORIES("/opt/MATLAB/R2012a/bin/glnxa64")
C.LINK_LIBRARIES命令添加需要链接的库文件路径,注意这里是全路径,比如
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
D.TARGET_LINK_LIBRARIES命令设置要链接的库文件名称,比如
TARGET_LINK_LIBRARIES(myProject hello),连接libhello.so库
TARGET_LINK_LIBRARIES(myProject libhello.a)
TARGET_LINK_LIBRARIES(myProject libhello.so)