CMAKE学习记录

1. 定义

  CMAKE是一个开源、跨平台的编译、测试和打包工具,它使用比较简单的语言描述编译、安装的过程,输出Makefile或者project文件,再去执行构建。
  当多人协同开发一个较大的项目时,会产生较多的源代码文件,因此需要说明编译的顺序,例如需要先编译什么 ,再编译什么,这个过程称之为构建(Build),在构建的过程中使用的工具是make,对应的定义构建过程的文件称为Makefile;但是编写Makefile文件的语法较为复杂,尤其是对于大型的复杂项目,编写Makefile的过程则更为困难。
  而Cmake为我们提供了一套简洁的语法去定义构建的流程,CMake定义构建过程的文件称为CMakeLists.txt。在使用IDE进行开发的过程中,这个流程一般是由IDE自动完成的,开发者基本不需要干预,但是如果开发者需要控制构建的细节,则需要自己定义构建过程。

2. gcc,make,cmake之间的关系

  • gcc能够将源代码文件编译成可执行文件或者共享库
  • 当编译的源文件较多的时候,需要指定编译的先后次序,这个过程称为构建(Build),使用的工具是make,定义构建过程的文件称为Makefile
  • 对于大型项目,编写Makefile的过程较为复杂,通过CMake,使用更加简洁的语法,就可以定义构建流程(也即生成Makefile),CMake定义构建流程的文件为CMakeLists.txt

3. CMake介绍以及使用

3.1 介绍

  CMake提供了cmake,ctest,cpack三个命令行工具,分别用于项目的构建,测试,打包。

1. PROJECT关键字

用于指定工程名,此外,还可以指定项目版本号和使用的语言

PROJECT(demon1)
PROJECT(demon1 VERSION 1.0.0 LANGUAGES C CXX)

与此同时,这条语句还隐式的定义了两个变量

PROJECT_BINARY_DIR   // 二进制文件目录
PROJECT_SOURCE_DIR   // 源代码目录
2. SET关键字

用于显式地定义一个变量,从而实现用变量代替一个或者多个值,例如:

# 定义变量
set(SRCLIST main.cpp)
set(SRCLIST main.cpp other.cpp)
3. MESSAGE关键字

主要用于向终端输出用户自定义的信息,输出信息可分为三类:

  • SEND_ERROR          表示产生错误,生成过程直接被跳
  • STATUS                     常用于查看变量值,类似于 DEBUG 级别信息。信息的前缀带 --
  • FATAL_ERROR          立即终止所有cake过程
MESSAGE(STATUS "Binary dir is: ${PROJECT_BINARY_DIR}")
4. LIST关键字

用于对列表进行一些列的操作,List命令的格式如下:

list (subcommand <list> [args...])
  • subcommand    子命令
  • <list>        需要操作的列表
  • agrs    参数

子命令的类型如下所示:

LENGTH            // 获取list的长度

GET              // 返回list中下标为index的element到value中

APPEND            // 添加新元素element到list中

FIND             // 返回list中element的index,没有找到返回-1

INSERT           // 将新element插入到list中index的位置

REMOVE_ITEM        // 从list中删除某个element

REMOVE_AT         // 从list中删除指定index的element

REMOVE_DUPLICATES       // 从list中删除重复的element

REVERSE           // 将list的元素的次序进行反转

SORT             // 将list按字母顺序进行排序

LIST使用实例:

# test list

SET(alist a b c d)

LIST(LENGTH alist len)

MESSAGE(STATUS "List length is ${len}")

LIST(GET alist 1 var)

MESSAGE(STATUS "The 1st element is ${var}")

LIST(APPEND alist hh)

MESSAGE(STATUS "The list is ${alist}")

LIST(FIND alist hh index)

MESSAGE(STATUS "The index of hh is ${index}")

LIST(FIND alist k index)

MESSAGE(STATUS "The index of k is ${index}")

输出结果:

CMAKE学习记录

5. ADD_EXECUTABLE关键字

通过指定的源文件列表构建出可执行目标文件,格式如下所示:

add_executable (&lt;name&gt; [WIN32] [MACOSX_BUNDLE]
      [EXCLUDE_FROM_ALL]
      [source1] [source2 ...])
  • name      可执行文件的名称
  • source    生成可执行文件的源代码文件

例如:

add_executable(main test1.cpp test2.cpp main.cpp)
6. AUX_SOURCE_DIRECTORY
aux_source_directory(<dir> <variable>)

查找指定目录下所有源文件,并将名称保存到变量中,当同一目录下有多个文件的时候,较为方便。使用set一个一个添加也可以达到相同的目的.但是aux_source_directory只能检测到目录下的.cpp文件,无法检测到.h文件

set(COMMON_KERNEL_HEADERS
    src/kernel/CommRequest.h
    src/kernel/CommScheduler.h
    src/kernel/Communicator.h
    src/kernel/SleepRequest.h
    src/kernel/ExecRequest.h
    src/kernel/IORequest.h
    src/kernel/Executor.h
    src/kernel/list.h
    src/kernel/mpoller.h
    src/kernel/poller.h
    src/kernel/msgqueue.h
    src/kernel/rbtree.h
    src/kernel/SubTask.h
    src/kernel/thrdpool.h
)

使用实例:简单的hello world程序 main.cpp和CMakeLists.txt在同一目录

main.cpp文件

#include <stdio.h>
#include <unistd.h>

/*
   unistd.h为Linux/Unix系统中内置头文件,包含了许多系统服务的函数原型,例如read函数、write函数和getpid函数等。
   其作用相当于windows操作系统的"windows.h",是操作系统为用户提供的统一API接口,方便调用系统提供的一些服务。
*/

int gval = 0;

int main(int argc, char** argv)
{
    printf("Hello worldn");

    return 0;
}

CMakeLists.txt文件

CMAKE_MINIMUM_REQUIRED(VERSION 3.1)

PROJECT(forkDemon VERSION 1.0.0 LANGUAGES CXX)    # projectName version languages

AUX_SOURCE_DIRECTORY(. DIR_SRCS)

ADD_EXECUTABLE(main ${DIR_SRCS})

# prinf some info
message(STATUS "This is binary dir: " ${PROJECT_BINARY_DIR})
message(STATUS "This is dource dir: " ${PROJECT_SOURCE_DIR})
message(NOTICE, "Finished build the project.")

执行如下命令:

mkdir build               // 创建build目录,进行外部编译,所有生成的内容都存放在build目录下,不影响源代码目录
cmake -S . -B ./build     // 构建 -S指定源代码目录,且需要有一个CMakeLists.txt  -B指定构建的目录
cmake --build ./build     // 执行构建
8. 输出路径设置
变量 含义
CMAKE_ARCHIVE_OUTPUT_DIRECTORY 指定存放静态库文件的位置
这一变量用来初始化ARCHIVE_OUTPUT_DIRECTORY
会自动创建debug或者release目录
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG 指定debug模式下存放静态库文件的位置
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE 指定release模式下存放静态库文件的位置
CMAKE_LIBRARY_OUTPUT_DIRECTORY 指定存放动态库文件的位置
这一变量用来初始化LIBRARY_OUTPUT_DIRECTORY
会自动创建debug或者release目录
CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG 指定debug存放动态库文件的位置
CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE 指定release存放动态库文件的位置
CMAKE_RUNTIME_OUTPUT_DIRECTORY 指定存放可执行文件的位置
这一变量用来初始化RUNTIME_OUTPUT_DIRECTORY
会自动创建debug或者release目录
CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG 指定debug模式下可执行文件的存放位置
CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE 指定release模式下可执行文件的存放位置
LIBRARY_OUTPUT_PATH 默认存放库文件的位置,如果没有指定CMAKE_ARCHIVE_OUTPUT_DIRECTORYCMAKE_LIBRARY_OUTPUT_DIRECTORY,生成的动态库文件和静态库文件将默认保存在此位置
EXECUTABLE_OUTPUT_PATH 指定可执行文件输出路径, 但是RUNTIME_OUTPUT_DIRECTORY的优先级更高
如果没有初始化RUNTIME_OUTPUT_DIRECTORY,则会保存在EXECUTABLE_OUTPUT_PATH所指定的目录中
CMAKE_DEBUG_POSTFIX 指定debug模式下生成的库文件的后缀
CMAKE_RELEASE_POSTFIX 指定release模式下生成的库文件的后缀名

使用例子:

# 编译产物的输出路径
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BIN_DIR}) 
9. ADD_SUBDIRECTORY关键字

用于向构建添加一个子目录,格式如下所示:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir   用于指定源CMakeLists.txt以及源代码文件所在的路径  (通常是相对路径)
  • binary_dir    用于指定输出文件所在的路径   (通常是相对路径)

注:在这个添加的SUBDIRECTORY中,必须有CMakeLists.txt文件以及源代码文件。在使用add_subdirectory的时候,如果source_dir不是当前CMakeLists.txt所在目录的子目录,则需要显示的指定binary_dir,用于存储source_dir相关文件。

10. ADD_LIBRARY 关键字

用于生成动态库或者静态库,默认是生成静态库,格式如下所示:

add_library(&lt;name&gt; [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [source1] [source2 ...])
  • name            生成库的名字,必须全局唯一
  • STATIC         指定生成的是静态库
  • SHARED      指定生成的是动态库
  • MODULE      指定生成模块库 (暂时没有用过)
  • source           指定生成库所需要的代码源文件

注:生成的library名会根据STATIC或SHARED成为动态库或者静态库,这里的STATIC和SHARED可不设置,通过全局的BUILD_SHARED_LIBS的FALSE或TRUE来指定,windows下,如果dll没有export任何信息,则不能使用SHARED,要标识为MODULE

使用实例:
新建代码目录以及代码源文件(Linux下)

CMAKE学习记录

 main.cpp文件

#include <stdio.h>
#include "math/mathfunc.h"      // 这里路径不全会导致报错,找不到相应的文件

int main(int agrc, char** argv)
{
    add(3,5);

    sub(9,3);

    mul(2,6);

    sub(12,4);

    return 0;
}

CMakeLsts.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.1)      # cmake version

# 设置工程名
PROJECT(calcluator VERSION 1.0.0 LANGUAGES CXX)   # projectName version languages

# 添加源文件 
AUX_SOURCE_DIRECTORY(. DIR_SRCS)

# 构建子目录
ADD_SUBDIRECTORY(./math PROJECT_BINARY_DIR)

ADD_EXECUTABLE(cal ${DIR_SRCS})

# 需要连接的库
target_link_libraries(cal mathfuncs)

MESSAGE(STATUS, "Finished build the project.")

math目录下的文件
mathfunc.h文件:

#ifndef MATHFUNC_H
#define MATHFUNC_H

extern int add(int x, int y);
extern int sub(int x, int y);
extern int mul(int x, int y);
extern int div(int x, int y);

#endif

mathfunc.cpp文件:

#include "mathfunc.h"
#include <limits.h>
#include <stdio.h>

int add(int x, int y)
{
    int result = x + y;

    printf("%d + %d = %dn", x, y, result);

    return result;
}

int sub(int x, int y)
{
    int result = x - y;

    printf("%d - %d = %dn", x, y, result);

    return result;
}

int mul(int x, int y)
{
    int result = x * y;

    printf("%d * %d = %dn", x, y, result);

    return result;
}

int div(int x, int y)
{
    if (y == 0)
    {
        printf("The devisor can not be zero.n");
        return INT_MIN;
    }

    int result = x / y;

    printf("%d / %d = %dn", x, y, result);

    return result;
}

CMakeLists.txt

# mathfuncs 的makeLists

AUX_SOURCE_DIRECTORY(. MATH_LIB_SRC)

# 生成指定的连接库
ADD_LIBRARY(mathfuncs SHARED ${MATH_LIB_SRC})

MESSAGE(STATUS "Generationg mathfunc lib")

开始进行构架以及编译:
在源代码更目录新建builld文件夹:

mkdir build 

cmake -S . -B ./build

cd build && make -j && cd -

编译以及运行结果:

CMAKE学习记录 ​​​​CMAKE学习记录

CMAKE学习记录

11. INCLUDE_DIRECTORIES关键字

  将指定的文件添加到编译器的头文件搜索路径之下,为编译器提供搜索头文件的路径,例如上面的cal实例中,在main.cpp中需要#include "math/mathfunc.h",显得比较繁琐,如果在CMakeLists.txt文件中为编译器提供了此路径作为搜索路径,则在源代码中直接#include "mathfunc.h"即可。

代码修改如下:
main.cpp

#include <stdio.h>
#include "mathfunc.h"      

int main(int agrc, char** argv)
{
    add(3,5);

    sub(9,3);

    mul(2,6);

    sub(12,4);

    return 0;
}

CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.1)      # cmake version

# 设置工程名
PROJECT(calcluator VERSION 1.0.0 LANGUAGES CXX)   # projectName version languages

# 添加源文件 
AUX_SOURCE_DIRECTORY(. DIR_SRCS)

# 为编译器指定头文件搜索路径
INCLUDE_DIRECTORIES(./math)

# 构建子目录
ADD_SUBDIRECTORY(./math PROJECT_BINARY_DIR)

ADD_EXECUTABLE(cal ${DIR_SRCS})

# 需要连接的库
target_link_libraries(cal mathfuncs)

MESSAGE(STATUS, "Finished build the project.")
12. CMake向程序中添加调试信息,可供GDB进行调试

需要向CMakeList.txt文件中添加如下信息:

SET(CMAKE_BUILD_TYPE "Debug")  
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")  
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
  • CMAKE_BUILD_TYPE:   为CMake的内建变量,可以取值为Debug,Release,RelWithDebInfo 和 MinSizeRel
  • CMAKE_BUILD_TYPE的值设置为Debug的时候,CMake会使用内建变量CMAKE_CXX_FLAGS_DEBUGCMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项来生成 Makefile。
  • CMAKE_BUILD_TYPE的值设置为Release的时候,CMake会使用内建变量CMAKE_CXX_FLAGS_RELEASECMAKE_C_FLAGS_RELEASE中的字符串作为编译选项来生成 Makefile。

CXXFLAGS: C++编译器的选项,无默认值
CFLAGS: C编译器的选项,无默认值
在设置编译选项的时候,还可以使用add_compile_options来对编译选项进行设置,区别是add_compile_options是针对所有编译器的(c和c++),而CXXFLAGS选项只针对c++编译器,CFLAGS选项只针对c编译器。

13. CMake中的find_Package

  为了方便的在项目中引人外部依赖包(第三方库),CMake官方提供了许多预定义的寻找依赖包的Module,这些Module所在的路径为/usr/share/cmake-3.16/Modules,如下所示:

CMAKE学习记录

find_package可以通过用户指定的库名称和版本,找到库的头文件路径,链接库路径,版本号等信息:

引入cmake官方提供的库,例如需要加载opencv库:

可通过如下命令查看cmake官方提供的库有哪些:

cmake --help-module-list | grep -E ^Find

Modules模式参数:

find_package(<package> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])

参数含义:

  • package 库名,区分大小写
  • version 指定库版本,如果指定,就需要检测找到的库和指定的version是否兼容
  • EXACT 找到的库版本必须和指定的version完全匹配
  • QUIET 查找失败,不会输出错误信息,指定REQUIRED的时候失效
  • MODULE cmake的默认工作模式是module,如果Module模式下失败,则会切换到config模式下,如果制定了Module,则只在Module模式下进行
  • REQUIRED 必须找到指定的库,如果找不到就会停止整个cmake流程,默认不会停止
  • COMPONENTS 表示查找到的库中必须包含的组件,如果没有,则查找失败
cmake_minimum_required(VERSION 2.8)
project(find_package_learning)
find_package(OpenCV 3 REQUIRED)

if(OpenCv_FOUND)
    message(STATUS "OpenCV_DIR = ${OpenCV_DIR}")
    message(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
    message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")

    include_directories(${OPENCV_INCLUDE_DIRS})     # 包含头文件
    add_executable(opencv_test opencv_test.cpp)     # 可执行文件
    target_link_libraries(opencv_test ${OpenCV_LIBS})  # 链接库
else(OpenCv_FOUND)
    message(FATAL_ERROR "OpenCv Not Found!")
endif(OpenCv_FOUND)

对于系统预定义的Find<LibName>.cmake模块,在执行find_package之后,每个模块都会定义以下几个变量:

  • <LibName>_FOUND 表示对应的库是否找到
  • <LibName>_INCLUDE_DIR or <LibName>_INCLUDES 表示对应的第三方库的头文件路径
  • <LibName>_LIBRARY or <LibName>_LIBRARIES表示第三方库的链接路径

输出:

-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found OpenCV: /usr/local (found suitable version "3.4.4", minimum required is "3") 
-- OpenCV_DIR = /usr/local/share/OpenCV
-- OpenCV_INCLUDE_DIRS = /usr/local/include;/usr/local/include/opencv
-- OpenCV_LIBS = opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_shape;opencv_stitching;opencv_superres;opencv_video;opencv_videoio;opencv_videostab;opencv_viz;opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib;opencv_cvv;opencv_datasets;opencv_dnn_objdetect;opencv_dpm;opencv_face;opencv_freetype;opencv_fuzzy;opencv_hdf;opencv_hfs;opencv_img_hash;opencv_line_descriptor;opencv_optflow;opencv_phase_unwrapping;opencv_plot;opencv_reg;opencv_rgbd;opencv_saliency;opencv_stereo;opencv_structured_light;opencv_surface_matching;opencv_text;opencv_tracking;opencv_xfeatures2d;opencv_ximgproc;opencv_xobjdetect;opencv_xphoto
-- Configuring done
-- Generating done
-- Build files have been written to: /home/zhangj/Programming/programming-learning-examples/cmake_learning/learn_cmake_easily/find_package_learning/build

引入cmake非官方库:(只对支持cmake编译安装的库有效)

Modules路径/usr/share/cmake-3.16/Modules下未定义的模块,需要用户先下载源码,然后通过Cmake编译安装,之后可通过与OpenCv一样的方式引入该库。

find_package有两种工作模式,一种是Modules模式,即上述引入OpenCv库的方式,另一种是Config模式。Config模式是指引入Module路径/usr/share/cmake-3.16/Modules下未定义的模块。此时需要用户下载对应第三方库的源码,进行编译,安装,再通过上述的方式进行引入;

如何为自己编写的库编写FindLibName.cmake文件

可参考如下教程:

https://blog.csdn.net/zhanghm1995/article/details/105466372
https://zhuanlan.zhihu.com/p/97369704

总结

  在Modules模式下,cmake需要找到一个叫做Find<LibName>.cmake的文件。这个文件负责找到对应的库所在路径,为我们的项目引入头文件路径和库文件路径。cmake搜索这个文件的路径有两个,一个是上文提到的cmake安装目录下的usr/share/cmake-<version>/Modules目录,另一个是用户指定的CMAKE_MODULE_PATH的所在目录(默认为空,用户可通过set来指定它的值),如果cmake没有找到对应的Find<LibName>.cmake文件,则会转入Config模式下进行工作。

  Config模式下主要通过<LibName>Config.cmake 或者 <lower-case-package-name>-config.cmake这两个文件来引入用户需要的第三方库;在安装库(支持cmake编译安装)之后,会自动在/usr/local/lib/cmake/<LibName>/目录下生成了LibName-config.cmake文件,而/usr/local/lib/cmake/<LibName>/目录正是find_package函数在Config模式下的搜索路径之一,这就保证了后续安装的库也能够通过find_package正确引入;

tips
Cmake在Config模式下,搜索包的路径依次为:

  • <LibName>_DIR 默认为空
  • CMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_APPBUNDLE_PATH 默认为空
  • PATH 环境变量路径

如果确知道想要查找的库Config.cmake或-config.cmake文件所在路径,为了能够准确定位到这个包,可以直接设置变量_DIR为具体路径,如:

set(OpenCV_DIR "/home/zhanghm/Softwares/enviroment_config/opencv3_4_4/opencv/build")

此时就可以明确找到需要的库

4 $ENV{}的作用是用于获取环境变量且设置环境变量的值

set($ENV{变量名} 变量值)
4.1编译选项设置

gcc的四个优化等级选项O0, O1, O2, O3,Os

gcc为了满足不同用户对于代码编译时候的优化需求,提供了近百种的优化选项供用户选择,从编译时间,目标文件长度,程序执行效率等多个方面进行优化。使用户可以通过不同的选择项组合,实现在多个维度对程序编译的优化进行取舍和平衡。由于编译优化选项过于多,使用不便且难度较大,因此gcc又为用户提供了O0, O1, O2, O3,Os等多个不同的优化等级,降低优化选项的使用难度。

gcc优化选项
优化等级 描述
O0 编译器默认的选项,对程序编译时不做任何优化
O1 对程序做部分优化,编译器会尝试减小生成代码的尺寸,以及缩短执行时间,但并不执行需要占用大量编译时间的优化流程。它主要对代码的分支,常量以及表达式等进行优化。
O2 比O1更高级的选项,在打开O1的所有选项的基础上,进行更多的优化,执行更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间,在增加了编译时间的基础上,提高了生成代码的执行效率。
O3 在打开了O2所有的优化的基础上进行进一步的优化,如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化,但是O3优化会使得程序调试变得更加的不可能,因为O3优化中对寄存器进行了优化,变量不再存放于原本的寄存器中。
Os 主要是对程序的尺寸进行优化,对程序代码的大小做更深层次的优化

通常各种优化都会打乱程序的结构,让调试工作变得无从着手,并且会打乱执行顺序,依赖内存操作顺序的程序需要做相关处理才能确保程序运行结果的正确性。

  • -ggdb : 用于生成使编译器生成符合GDB专用的更为丰富的调试信息
  • -std=c++11 : 设置使用c++11标准
  • -Wall : 告知编译器编译完成后显示所有警告信息,与-W选项功能类似。与之相反的是-w选项,表示关闭警告信息显示

5. CMake为项目传入版本号,进行版本管理

5.1 在源代码根目录中新建config.h.in文件,文件内容如下:
#ifndef CONFIG_H_IN
#define CONFIG_H_IN

// VERSION 1.x.x 

#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER  "@PROJECT_VERSION@"
#define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@"
#define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@"
#define PTOJECT_VER_PATCH "@PROJECT_VERSION_PATCH@"

#endif 
5.2 在CMakeLists.txt里面设置项目名称,以及版本等信息,并指定配置文件
PROJECT("demon" VERSION 1.1.2)
CONFIGURE_FILE(config.h.in config.h)
5.3 在编译完成之后,就会在PROJECT_BINARY_DIR里面生成对应的config.h文件,然后在CMakeLists.txt中加上引用目录,再在程序中引用即可。
INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR})

应用实例:(以hello word的)

config.h.in文件:

#ifndef CONFIG_H_IN
#define CONFIG_H_IN

// VERSION 1.x.x 

#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER  "@PROJECT_VERSION@"
#define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@"
#define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@"
#define PTOJECT_VER_PATCH "@PROJECT_VERSION_PATCH@"

#endif 

CMakeLists.txt文件

CMAKE_MINIMUM_REQUIRED(VERSION 3.1)      # cmake version

PROJECT(demon1 VERSION 1.2.1 LANGUAGES CXX)   # projectName version languages

# 配置文件
CONFIGURE_FILE(config.h.in config.h)

# 生成的配置文件在PROJECT_BINARY_DIR中,需要设置包含目录
INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR})

AUX_SOURCE_DIRECTORY(. DIR_SRCS)

ADD_EXECUTABLE(main ${DIR_SRCS})

MESSAGE(NOTICE, "Finished build the project.")

main.cpp文件

#include <stdio.h>;
#include <unistd.h>;
#include <config.h>;

int main(int argc, char** argv)
{
    printf("The program version is: %sn", PROJECT_VER);
    printf("The Major Program is: %sn", PROJECT_VER_MAJOR);
    printf("The Minor Program is: %sn", PROJECT_VER_MINOR);
    printf("The Path  Program is: %sn", PTOJECT_VER_PATCH);
    printf("Hello worldn");
    return 0;
}

运行结果:
生成的config.h文件

#ifndef CONFIG_H_IN
#define CONFIG_H_IN

// VERSION 1.x.x 

#define PROJECT_NAME "demon1"
#define PROJECT_VER  "1.2.1"
#define PROJECT_VER_MAJOR "1"
#define PROJECT_VER_MINOR "2"
#define PTOJECT_VER_PATCH "1"

#endif

CMAKE学习记录

6. 安装

cmake也可以指定安装规则,可以在产生MakeFile之后,通过make install来执行,它可以用来安装包括目标二进制,动态库,静态库,目录以及脚本等内容。

6.1 install指令
install(TARGETS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
6.1.1 安装目标文件
 install(TARGETS targets... [EXPORT <export-name>]
        [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
          PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
         [DESTINATION <dir>]
         [PERMISSIONS permissions...]
         [CONFIGURATIONS [Debug|Release|...]]
         [COMPONENT <component>]
         [NAMELINK_COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
         [NAMELINK_ONLY|NAMELINK_SKIP]
        ] [...]
        [INCLUDES DESTINATION [<dir> ...]]
        )

参数中的Target可以是多种目标文件,如二进制可执行文件,动态库,静态库

目标文件(Targets) 安装目录变量 安装目录
ARCHIVE ${CMAKE_INSTALL_LIBDIR} /usr/local/lib
LIBRARY ${CMAKE_INSTALL_LIBDIR} /usr/local/lib
RUNTIME ${CMAKE_INSTALL_BINDIR} /usr/local/bin
PRIVATE_HEADER ${CMAKE_INSTALL_INCLUDEDIR} /usr/local/include
PUBLIC_HEADER ${CMAKE_INSTALL_INCLUDEDIR} /usr/local/include

其他参数的含义:

  • DESTINATION : 指定要安装的目录
  • PERMISSIONS : 指定要安装文件的权限,有效的权限分别是:
    • OWNER_READ
    • OWNER_WRITE
    • OWNER_EXECUTE
    • GROUP_READ
    • GROUP_WRITE
    • GROUP_EXECUTE
    • WORLD_READ
    • WORLD_WRITE
    • WORLD_EXECUTE
  • CONFIGURATIONS : 指定安装规则适用的构建配置列表 (debug / release)
  • EXCLUDE_FROM_ALL : 指定该文件从完整安装中排除
  • OPTIONAL : 如果安装的文件不存在,则指定不作为错误

使用实例:

INSTALL(TARGETS myRun
        RUNTIME DESTIONATION ${CMAKE_INSTALL_BINDIR}
        PERMISSION OWNER_READ OWNER_WRITE OWNER_EXECUTE
        OPTIONAL)
6.1.2 安装普通文件
install(<FILES|PROGRAMS> files...
        TYPE <type> | DESTINATION <dir>
        [PERMISSIONS permissions...]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>]
        [RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])

安装普通文件的时候,如果 FILE|PORGRAMS为相对路径,则会相对于当前源目录给出解释,推荐使用GUN标准安装变量进行安装。

  • FILE 普通文件
  • PROGRAMS 非目标文件的可执行程序(如脚本)

其中参数TYPE可选的类型如下所示:

文件类型(Type) 安装目录变量 安装目录
BIN ${CMAKE_INSTALL_BINDIR} /usr/local/bin
SBIN ${CMAKE_INSTALL_SBINDIR} /usr/local/sbin
LIB ${CMAKE_INSTALL_LIBDIR} /usr/local/lib
INCLUDE ${CMAKE_INSTALL_INCLUDEDIR /usr/local/include
SYSCONF ${CMAKE_INSTALL_SYSCONFDIR /usr/local/etc
SHAREDSTATE ${CMAKE_INSTALL_SHARESTATEDIR} /usr/local/com
LOCALSTATE ${CMAKE_INSTALL_LOCALSTATEDIR} /usr/local/var
RUNSTATE ${CMAKE_INSTALL_RUNSTATEDIR} /usr/local/run
INFO ${CMAKE_INSTALL_INFODIR} /usr/local/info
LOCALE ${CMAKE_INSTALL_LOCALEDIR} /usr/local/locale
MAN ${CMAKE_INSTALL_MANDIR} /usr/local/man
DOC ${CMAKE_INSTALL_DOCDIR} /usr/local/etc/doc

其他参数含义:

  • COMPONENT: 含义?
  • 其他参数含义与上述相同
6.1.3 目录的安装
install(DIRECTORY dirs...
        TYPE <type> | DESTINATION <dir>
        [FILE_PERMISSIONS permissions...]
        [DIRECTORY_PERMISSIONS permissions...]
        [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>] [EXCLUDE_FROM_ALL]
        [FILES_MATCHING]
        [[PATTERN <pattern> | REGEX <regex>]
         [EXCLUDE] [PERMISSIONS permissions...]] [...])

该命令可以将一个或多个目录的内容安装到指定的位置,需要注意的是,如果目录不以/结尾,则这个目录将被安装到目标路径下;如果目录以/结尾,则代表这个目录下的全部内容被安装到目录路径下,但是不包括这个目录

例如,DIRECTORY test,表示将test目录安装到目标路径,DIRECTORY test/ 表示将test目录下所有的内容安装到目标路径,但是不包括test目录。

权限:

  • FILE_PERMISSIONS 指定文件的权限
  • DIRECTORY_PERMISSIONS 指定目录的权限

可以通过PATTERNREGEX 选项以精细的力度控制目录和文件的安装,可指定一个通配模式或者正则表达式以匹配安装的文件或者目录。PATTERN 仅匹配完整的文件名,而REGEX 可匹配文件名或目录的任何部分。

常用的两个跟随PATTERNREGEX的参数:

  • EXCLUDE 跳过匹配的文件或者目录
  • PERMISSION 指定匹配的文件或者目录的权限

例子:

install(DIRECTORY icons scripts/ DESTINATION share/myproj
        PATTERN "CVS" EXCLUDE
        PATTERN "scripts/*"
        PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
                    GROUP_EXECUTE GROUP_READ)

命令的含义: 将目录icons和script目录下的所有内容安装到指定目录share/myproj下,安装时候跳过名为CSV的子目录,且设置scripts目录下所有文件的权限为OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ。

6.2 在GUN/Linux中使用 GNUInstallDirs 优化 cmake 安装路径

  在使用cmake指定安装路径的时候,应该使用变量来指定安装路径,而不是将安装完全写死,以便于在不完全符合FHS的系统上进行安装。GUN提出了适用于unix系统的GUN标准安装目录,GUN/Linux使用的是这套标准的变体。cmake 官方提供了 GNUInstallDirs 模块,定义了一组标准的变量,用于安装不同类型文件到规范指定的目录中。

  要使用此模块,仅需要在CMakeLists.txt文件中添加如下语句:

include(GNUInstallDirs)

如果在编写CMakeLists.txt过程中,发现CMAKE_INSTALL_XXXX变量为空,则应该是缺少该include语句。

cmake在GUNInstallDirs模块中,定义了一些列变量用于表示GUN/Linux标准安装目录,这些值可以传递给install()命令的DESTINATION选项,来指定相应文件的安装路径。

变量名 变量含义
CMAKE_INSTALL_PREFIX 一个特殊的变量,在包含了GUNInstallDirs模块之后,这个变量的
值会被初始化,后续安装过程中,所有相对路径都会将这个变量的
值作为前缀,进行拼接,形成安装的绝对路径,按照GUN的标准,
其默认值为 /usr/local
CMAKE_INSTALL_BINDIR 可执行程序目录 CMAKE_INSTALL_PREFIX/bin
CMAKE_INSTALL_SBINDIR 系统管理员可执行程序目录 CMAKE_INSTALL_PREFIX/sbin
CMAKE_INSTALL_LIBDIR 目标代码库 CMAKE_INSTALL_PREFIX/lib
CMAKE_INSTALL_INCLUDEDIR c语言头文件 CMAKE_INSTALL_PREFIX/include
CMAKE_INSTALL_MANDIR man文档目录 CMAKE_INSTALL_PREFIX/man
CMAKE_INSTALL_SYSCONFDIR 配置文件目录 CMAKE_INSTALL_PREFIX/etc
(单机只读数据,read-only single-machine data)
CMAKE_INSTALL_DATAROOTDIR 与架构无关的只读数据根目录 CMAKE_INSTALL_PREFIX/share
CMAKE_INSTALL_DOCDIR 文档根目录 CMAKE_INSTALL_DATAROOTDIR/doc

7. 测试

原文链接: https://www.cnblogs.com/ncepubye/p/17023364.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    CMAKE学习记录

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/396350

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月6日 上午11:16
下一篇 2023年4月6日 上午11:17

相关推荐