CMakeLists编写快速实现技巧

简单工程编写

单一生成文件

以下就是一个最简单的工程编写的内容
代码内容如下:

#include <iostream>
using namespace std;
int main()
{
    
    
    cout << "test cmake" << endl;
}

通常情况下:

g++ -o B main.cc

只需执行上述一行即可,但是因为大型工程再这样手动编译,显然不是一个好选择。
由于代码比较简单不涉及三方库或者系统库,也是单一文件夹,不涉及嵌套编译,此时编写CMakeLists.txt也比较容易。

cmake_minimum_required(VERSION 3.12)  #cmake必备,指明cmake下限版本需求,一般结合自己cmake版本即可
project(A)#工程名称,注意工程名称不是生成文件的名称

#设置编译器(一般是由系统默认可以不写的,也可以人为指定编译器版本)
#SET(CMAKE_C_COMPILER "/usr/local/bin/gcc")
#SET(CMAKE_CXX_COMPILER "/usr/local/bin/g++")
add_executable(B #生成文件名称
  main.cc #生成文件B所需的源文件(只需要源文件,不需要指明头文件)
)

将main.cc文件和CMakeLists.txt文件放在同一文件夹下,然后创建一个新的文件夹build,形成如下的结构:
.
├── build
├── CMakeLists.txt
└── main.cc
此时,打开终端,执行如下步骤,即可完成编译运行

cd build #进入编译文件夹
cmake .. #构建Cmake
make #编译
./B #运行生成文件B

多个生成文件

与Qt不同,CMake可以同时编译多个生成文件,多文件生成的工程文件CMakeLists.txt也很好编写。
工程文件结构如下:
.
├── build
├── CMakeLists.txt
├── main1.cc
└── main.cc
多个main文件时的CMakeLists.txt的编写如下:

cmake_minimum_required(VERSION 3.12)  
project(A)

add_executable(B
  main.cc
)
add_executable(C #多个生成文件,只需
  main1.cc
)

然后进行编译运行即可,步骤同上。

编译选项

程序不可能依赖标准库,偶尔系统的一些库也是需要编译选项的。
例如:

g++ -o B main.cc
g++ -o B main.cc -g -Wall

其中-lz,-pthread就是编译选项,-g是支持gdb调试,-Wall选项意思是编译后显示所有警告。
如何添加编译选项呢?

cmake_minimum_required(VERSION 3.12)  
project(npy2png)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall   -g") #C编译添加编译选项   @1
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -g")#c++编译添加编译选项   @2

add_executable(NpyToPng
  main.cc
)

通过上述代码,可以看到@1和@2就是添加编译选项的方法,一般情况下只写一行就行,但是大型工程C和C++混写已经很常见了,省事儿起见,两行都写就行。此时进行编译生成就能看到warning信息,并且支持gdb调试了。

增加动态依赖库

link_directories(
    /usr/lib/x86_64-linux-gnu/
    #添加依赖路径
)
#其它内容
#add_library或者add_excutable
target_link_libraries(${PROJECT_NAME}
    libz.so
    #添加依赖库
)

这里只写添加方法,后续的例子可以看到应用的实例,就不专门写个例子了。

复杂工程编写

第三方依赖库

文件结构如下:
.
├── CMakeLists.txt
├── cnpy.cpp
├── cnpy.h
└── main.cpp
其中cnpy需要libz.so,且该动态库系统自带。
main需要OpenCV的库,该库为三方编译,所在文件路径为:
/home/freja/so_slam3rd/
该三方库的文件结构如下所示
/home/freja/so_slam3rd/
├── bin #文件
├── include #头文件
├── lib #动态库
└── share #共享库
本地待编译的文件夹的结构是一个main文件依赖cnpy文件,cnpy文件依赖libz。同时,main文件还依赖OpenCV。因此需要参照上一小节的增加依赖项。

cmake_minimum_required(VERSION 3.12)
project(npy2png)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall   -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -g")

find_package(OpenCV REQUIRED)#寻找三方库,这样OpenCV_LIBS变量里才会有动态库,否则该变量为空
# message(STATUS "-----------${OpenCV_LIBS}")#打印变量

#头文件路径添加       @1
include_directories(
  ${CMAKE_PREFIX_PATH}/include
)
#动态库路径添加       @2
link_directories(
  ${CMAKE_PREFIX_PATH}/lib
  /usr/lib/x86_64-linux-gnu/
)

add_executable(NpyToPng
  cnpy.cc
  main.cc
)

#链接动态库(链接动态库的顺序推荐和动态库路径保持对齐)  @3
target_link_libraries(NpyToPng
  ${OpenCV_LIBS}
  libz.so
)

此时,需要注意CMAKE_PREFIX_PATH是Cmake的编译选项,可以通过在cmake的时候指定CMAKE_PREFIX_PATH=“你所需的路径”。如果不嫌麻烦,OpenCV_LIBS也可以指定编译选项。但是opencv内动态库众多,避免一个一个敲键盘find_package显然是个好用法。
但是这有引来一个问题需要将该路径添加到系统路径内,也就是每次都要export到LD_LIBRARY_PATH这个环境变量中。
此时编译一遍,就需要再终端敲好几行内容;运行一遍,就需要再终端敲好几行内容。这显然不适合傻瓜编译。
因此,写个bash就是个好方法。分别写个编译和运行的bash就行。文件内容可以如下所示。
------------build.sh------------

#!/bin/bash
CUR_DIR=$(pwd)
echo ${CUR_DIR}
CUR_PATH=/home/freja/so_slam3rd/
echo ${CUR_PATH}
rm -rf build
mkdir build
cd build
mkdir data
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${CUR_PATH}
cmake -DCMAKE_PREFIX_PATH=${CUR_PATH} ..
make

------------run.sh------------

#!/bin/bash
CUR_PATH=/home/freja/so_slam3rd/
echo ${CUR_PATH}
cd build
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${CUR_PATH}
./NpyToPng  

  • 关于pcl库的集合名是PCL_LIBRARIES,opencv库的集合名是OpenCV_LIBS。我也不太明白,可能要看库的官方说明吧,平时targetlink的时候可以直接写xxx.so或者xxx。

嵌套编译(多文件夹)

.
├── app
│ ├── CMakeLists.txt
│ └── main.cpp
├── cnpy
│ ├── CMakeLists.txt
│ ├── cnpy.cpp
│ └── cnpy.h
├── lib
├── CMakeLists.txt
├── run.sh
└── build.sh
这个就是嵌套编译的文件结构,一般会将生成物单独放一个文件夹(app),功能模块开一个或者多层嵌套的文件夹(cnpy),功能模块通常是要生成动态库so文件的,所以这个生成库会单独放在lib文件件下,其它生成物随着build文件夹内生成即可。
在这个例子中共有3个CMakeLists文件=1个顶层CMakeLists+2个文件夹内部CMakeLists
此处需要注意的是最上层CMakeLists是统领所有文件编译的顶层CMakeLists文件。
它通常会部署环境变量,搜索依赖库,添加内部生成库,添加需要编译的子文件夹等功能。实例如下:

cmake_minimum_required(VERSION 3.12)
project(npy2png)
#设置编译选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall   -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -g")

#搜索依赖库
find_package(OpenCV REQUIRED)

#添加内部生成so库的头文件
include_directories(
  ${CMAKE_SOURCE_DIR}/cnpy
)
#添加内部生成的so文件库
link_directories(
  ${CMAKE_SOURCE_DIR}/lib
)
#添加需要编译的子文件夹
add_subdirectory(cnpy)
add_subdirectory(app)

两个文件夹内部CMakeLists也是有所不同,一个是生成可执行文件,一个是生成库。生成库的CMakeLists主要功能是配置依赖,表明生成库,安装生成库等
实例如下:

cmake_minimum_required(VERSION 3.12.0)
project(cnpy)
#无需设置编译选项,顶层已经设置了
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g")

link_directories(
    /usr/lib/x86_64-linux-gnu/
)

include_directories(
    ${CMAKE_PREFIX_PATH}/include
)
#表明生成库
add_library(${PROJECT_NAME} SHARED
    cnpy.cpp
)

target_link_libraries(${PROJECT_NAME}
    libz.so
)
#安装生成库
INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_SOURCE_DIR}/lib)

可执行文件的CMakeLists的编写与之前非常相似,就是有些细节需要变动
实例如下:

cmake_minimum_required(VERSION 3.12)
project(npy2png)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall   -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -g")


include_directories(
  ${CMAKE_PREFIX_PATH}/include
  ${CMAKE_SOURCE_DIR}/cnpy#主要区别1:内部库头文件路径
)

link_directories(
  ${CMAKE_PREFIX_PATH}/lib
  ${CMAKE_SOURCE_DIR}/lib#主要区别2:内部库so文件路径
)

add_executable(NpyToPng
  main.cpp
)
target_link_libraries(NpyToPng
  ${OpenCV_LIBS}
  cnpy#主要区别3:内部库so文件名
)

猜你喜欢

转载自blog.csdn.net/sinat_21699465/article/details/124041120