CMakeLists入门

背景

最近在编写各个C++项目时,经常用到CMakeLists.txt,今天花了半天时间进行了初步学习,现有一个test工程目录,内部的src及工程文件夹下各有一个cpp文件如图示。
 
example
├── build
├── CMakeLists.txt
├── include
│ └── Detection.h
├── libs
├── main.cpp
└── src
├── CMakeLists.txt
└── Detection.cpp

直接编写CMakeLists.txt,注释已经比较清楚了如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
cmake_minimum_required(VERSION 2.8.12)

project(examples)

# find_package()该指令会从8个系统路径查找包,但事先指定CMAKE_PREFIX_PATH(优先级最高的查找路径),再去找包,保证可以万无一失,就跟在ccmake中指定一样的道理。

list(APPEND CMAKE_PREFIX_PATH "/usr/local/Cellar/opencv/3.4.1_2")
find_package(OpenCV)


# message("OpenCV_INCLUDE_DIRS is : ${OpenCV_INCLUDE_DIRS}")
# message("OpenCV_LIBRARIES is ${OpenCV_LIBS}")

# PROJECT_SOURCE_DIR 跟 CMakeLists.txt文件位置保持一致
# message("SOURCE_DIR is ${PROJECT_SOURCE_DIR}")
# message("BINARY_DIR is ${PROJECT_BINARY_DIR}")

aux_source_directory(. SRC_LIST) # 当前目录下的所有源文件
message("${SRC_LIST}")

# where to find Headers
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${OpenCV_INCLUDE_DIRS})

# set 显示的指定变量,把多个cpp文件指定到src_files中
set(src_files
main.cpp
)

# 将src子目录加入工程,并指定编译输出路径为bin 目录。如果不指定bin目录,编译结果都将存放在build/src目录,通过这种方式生成的静态库不需要再link_directories。

# src目录有自己的CMakelists,指导其编译

ADD_SUBDIRECTORY(src bin)

# 或者先编译src,将生成的静态库放入libs目录,再通过下式link即可
# link_directories(${PROJECT_SOURCE_DIR}/libs)

# pack objects to static library
add_library(main STATIC ${src_files})

add_executable(demo main.cpp)

target_link_libraries(demo ${OpenCV_LIBS} detect)

# ps:
# 1,变量使用${}方式取值,但是在IF控制语句中是直接使用变量名
# 2,指令(参数1 参数2...) 参数使用括弧括起,参数之间使用空格或分号分开,风格统一即可
# 3,指令是大小写无关的,参数和变量是大小写相关的。

src文件夹下的CPP也需要一个CMakeLists.txt进行编译,它的内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
cmake_minimum_required(VERSION 2.8.12)

project(detect) # 如果此处未重新定义project,那么${PROJECT_SOURCE_DIR}指的是上一级cmake所指的目录

include_directories(${PROJECT_SOURCE_DIR}/../include)

set(src_files
Detection.cpp
)

SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../libs) # 注意这里变量名后加'/'

add_library(detect STATIC ${src_files})

一点顿悟

笔者本身做算法研发的,最近接触了一些SDK编写方面的任务,调试CMakeLists的动态、静态库时,突然就想明白了二者的区别。
 
在打包成exe时,静态库会和代码一起打包,这样给到其他用户时基本下载即可使用,无需下载opencv等“乱七八糟”的东西。而动态库则不行,动态库的exe并没有将其打包,只是留了动态库的借口。换句话说,用户使用你的demo时,一定要指到对应的第三库上。比如opencv3.4.1,要么他自己安装一份,要么你把用到的opencv打包一起传给他使用。
 
那他们的优缺点是什么呢?静态库虽然使用方便,但每个demo都需要一份独有的库(因为已经被打包到一起了),这样就会造成一定的空间浪费。而动态库,英文也叫”share lib”,每个demo可以共享同一份动态库,需要时调用对应的库便可以。对于Mac中,动态库的路径是绝对路径,给笔者最初的调试也造成了很大的困扰。而Linux中,动态库使用相对路径,只要指到对应的库就好。

Eg:

  • 本机:/home/wuxj/opencv/lib/libxxx.so
  • 其他用户:/home/zhaorui/opencv/lib/libxxx.so
  • Linux系统,libxxx.so吻合就ok;而Mac必须动态库路径才行,显然这样的指向,Mac就Error了

Linux中常用动态库的方法是指定库搜索路径,export LD_LIBRARY_PATH=<Path_to_libs>:$LD_LIBRARY_PATH
 
另外需要注意在CMakelists中带版本的动态库,需要指向其不带版本的软连接,因为ld查找时就是基于规则libxxxxx.so的查找。而cmake生成带版本的动态库时,会自动生成其相应软连接。