CMake 初级入门

1 cmake 简介

        cmake通过读取脚本文件CMakeLists.txt来构建编译系统。

        cmake使用CMakeLists.txt中的规则来生成Makefile文件,最终通过make生成可执行程序或库文件,但是cmake的语法比Makefile简单得多。

2 cmake 安装

sudo apt install cmake build-essiential

3 cmake 规则

        1、cmake默认使用CMakeLists.txt作为脚本文件构建编译系统。就像make默认使用Makefile一样;

        2、在项目顶层CMakeLists.txt的第一行为cmake_minimum_required(VERSION 3.5),指定了cmake的最小版本。因为在一些功能在低版本的cmake中是没有的,比如target_link_libraries()在3.13版本才开始支持;

        3、使用out of source方式构建系统。由于cmake在编译过程中会产生很多中间文件,所以一般会单独新建build文件夹来存储cmake产生的中间文件;

        4、cmake对变量大小写敏感,使用关键字set定义和设置变量值,通过${varname}的方式使用变量。

4 cmake 中的“hello world”

        首先,书写CMakeLists.txt,为cmake提供编译规则和依赖

cmake_minimum_required(VERSION 3.5)  # cmake最小版本要求

add_executable(helloworld helloworld.cpp)  # 可执行文件名 源文件名

        然后,新建build文件夹用来存储cmake的中间文件

cd your_workspace  # 进入工作空间
mkdir build        # 新建build文件夹
cd build           # 进入build文件夹
cmake ..           # 在上层目录执行cmake(CMakeLists.txt在上层目录中)
make               # 根据产生的Makefile执行make

        最后,运行build中产生的可执行文件helloworld

./helloworld  # 运行可执行文件

5 一个简单的小工程

        假设工程目录如下

|----CMakeLists.txt
|----main.cpp
|----hello
     |----CMakeLists.txt
     |----hello.cpp
     |----hello.h
|----world
     |----CMakeLists.txt
     |----world.cpp
     |----world.h

# 工程文件有hello和world两个子目录
# main.cpp调用hello/hello.cpp和world/world.cpp中的函数

        整个工程文件体现的是模块化思想,在hello和world两个子目录中也有CMakeLists.txt文件。当然可以将hello.cpp/.h和world.cpp/.h文件直接放在工程文件目录下,只使用一个CMakeList.txt,但是这样比较混乱(其实大部分开源工程都是直接放在工程文件目录下的...)

        现在看一下这3个CMakeLists.txt文件:

        @ ./ CMakeLists.txt

cmake_minimum_required(VERSION 3.5) 

project(helloworld)                           # 项目名称,可通过${PROJECT_NAME}来访问

add_subdirectory(hello)                       # 子目录subdir,必须包含CMakeLists.txt
add_subdirectory(world)

include_directories(hello world)

# aux_source_directory(dir var) 将dir下的所有源文件赋值给变量var,后续通过${var}来访问
aux_source_directory(. MAIN_SRC_FILES)  

add_executable(${PROJECT_NAME} ${MAIN_SRC_FILES}) 

# target_link_libraries(target lib) 将库文件lib链接到可执行文件(或库文件)target
# 库文件lib可以通过subdir中CMakeLists.txt的add_library()生成,或是第三方库如Eigen、Sophus
target_link_libraries(${PROJECT_NAME} hello)
target_link_libraries(${PROJECT_NAME} world)                     

        @ ./hello/CMakeLists.txt

project(hello)

aux_source_directory(. HELLO_SRC_FILES)

# add_library(target srcs) 指定源文件srcs生成目标文件target,目标文件是一个静态库
# add_library(target SAHRED srcs) 生成动态库(默认)
# add_library(target STATIC srcs) 生成静态库
add_library(${PROJECT_NAME} ${HELLO_SRC_FILES})

        @ ./world/CMakeLists.txt

project(world)

aux_source_directory(. WORLD_SRC_FILES)

add_library(${PROJECT_NAME} ${WORLD_SRC_FILES})

        可以注意到,在3个CMakeLists.txt文件里都有project(name),这是遵循就近原则的,都可以通过${PROJECT_NAME}访问。

6 add_subdirectory()命令

        add_sybdirectory()多用于添加下级目录。但是在有些情况下,多个下级目录之间可能会相互调用甚至会对上级目录调用。所以像 5 中简单地使用add_sybdirectory()是不对的,需要使用第二个参数--binary_dir,具体语法如下:

# 生成的subdirectory目标文件存储在binary_dir指定的目录中
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

        假设另外一种目录结构,test、hello、world同级

|----CMakeLists.txt
|----main.cpp
|----test
     |----CMakeLists.txt
     |----test.cpp
|----hello
     |----CMakeLists.txt
     |----hello.cpp
     |----hello.h
|----world
     |----CMakeLists.txt
     |----world.cpp
     |----world.h

        hello/:生成libhello.so 动态库

        world/:生成libworld.so 动态库

        test/:测试上述两个库是否可用

        现在我们要书写test/CMakeLists.txt文件,既然test要调用hello和world中的库,那么必然要使用include_directories()命令和add_subdirectory()命令,使得test包括hello和world,但是简单地使用add_subdirectory()是不对的,必须要使用第二参数--binary_dir。那么为啥 5 可以直接简单使用add_subdirectory()呢?是因为 5 中是上级目录包含下级目录hello和world。

        以下是正确的 test/CMakeLists.txt文件书写形式

cmake_minmun_required(VERSIOIN 3.5)

project(test)
set(TOP_DIR ../)  # 赋值
include_directories(${TOP_DIR}/hello ${TOP_DIR}/world)

# 错误写法,test与hello和world是同级的
# add_subdirectory(${TOP_DIR}/hello)
# add_subdirectory(${TOP_DIR}/hello)

# 正确写法,将subdir生成的库文件*.so,存储在build/*_binary_dir文件中
add_subdirectory(${TOP_DIR}/hello hello_binary_dir)
add_subdirectory(${TOP_DIR}/hello world_binary_dir)

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} hello)
target_link_libraries(${PROJECT_NAME} world)

        其实还有另外一种写法,就是不强求使用add_subdirectory(),毕竟test与hello和world是同级关系,可以在顶级目录./CMakeLists.txt中使用add_subdirectory(test)生成test中的可执行程序,同时将test/CMakeLists.txt中的两个add_subdirectory()去掉,只保留include_directories()。

7 指定库和可执行程序的生成目录

        根据 6,正常编译后,在build/中生成了许多文件,这些文件都是以各个PROJECT_NAME命名的,整个build/包含

./build/
|----CMakeCache.txt
|----CMakeFiles
|----cmake_install.cmake
|----Makefile
|----helloworld  # 可执行文件
|----test/
     |----CMakeFiles
     |----cmake_install.cmake
     |----Makefile
     |----test  # 可执行文件
|----hello/
     |----CMakeFiles
     |----cmake_install.cmake
     |----Makefile
     |----libhello.so  # hello动态库
|----world/
     |----CMakeFiles
     |----cmake_install.cmake
     |----Makefile
     |----libworld.so  # world动态库

        当然,我们可以手动设置可执行文件、动态库和静态库的位置(假设hello生成动态库,world生成静态库)

cmake_minimum_required(VERSION 3.5) 

project(helloworld)                           

# CMAKE_LIBRARY_OUTPUT_DIRECTORY 动态库目录
# CMAKE_ARCHIVE_OUTPUT_DIRECTORY 静态库目录
# CMAKE_RUNTIME_OUTPUT_DIRECTORY 可执行文件目录
# CMAKE_SOURCE_DIR 主目录
# CMAKE_CURRENT_LIST_DIR 当前CMakeLists.txt所在目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/output/lib-so)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/output/lib-ar)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/output/bin)

add_subdirectory(test)
add_subdirectory(hello)                      
add_subdirectory(world)

include_directories(hello world)

aux_source_directory(. MAIN_SRC_FILES)  

add_executable(${PROJECT_NAME} ${MAIN_SRC_FILES}) 

target_link_libraries(${PROJECT_NAME} hello)
target_link_libraries(${PROJECT_NAME} world) 

        正常编译后,产生目录如下

./
|----build/
     |---- ...
|----output/
     |----bin/
          |----helloworld
          |----test
     |----lib-ar
          |----libworld.a
     |----lib-so
          |----libhello.so

8 编译可视化技巧

        消息打印,可测试变量名称是否正确

message(STATUS "variable value: ${var-name}")

        显示具体编译过程,比如链接了哪些库文件、指定了哪些头文件路径等。有两种方法:

        1)在CMakeLists.txt中设置

set(CMAKE_VERBOSE_MAKEFILE ON)

        2)make编译时,使用make指令后缀

make VERBOSE=1

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值