以下为个人学习笔记整理。B 站课程传送门

# CMake

CMake 是用来统一编译代码,相当于是执行编译的自动化脚本。定义 CMakeLists.txt 以后,就不需要对项目代码进行逐个的编译。

# 编译一个简单代码

main.cpp

#include <iostream>
int main(){
    std::cout << "hello world" << std::endl;
    return 0;
}

CMakeLists.txt

project(CMakeTest)
set(SRC_LIST main.cpp)
message(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
message(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
add_executable(hello ${SRC_LIST})

在命令行执行 cmake . 表示运行当前目录下的 CMakeLists.txt

output

-- The C compiler identification is GNU 7.3.1
-- The CXX compiler identification is GNU 7.3.1
-- Check for working C compiler: /usr/lib64/ccache/cc
-- Check for working C compiler: /usr/lib64/ccache/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/lib64/ccache/c++
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is BINARY dir /data/rwbyguo/cmake_test
-- This is SOURCE dir /data/rwbyguo/cmake_test
-- Configuring done
-- Generating done
-- Build files have been written to: /data/rwbyguo/cmake_test

会发现多出来了几个文件

.
├── CMakeCache.txt
├── CMakeFiles
│   ├── 3.17.2
│   │   ├── CMakeCCompiler.cmake
│   │   ├── CMakeCXXCompiler.cmake
│   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   ├── CMakeSystem.cmake
│   │   ├── CompilerIdC
│   │   │   ├── a.out
│   │   │   ├── CMakeCCompilerId.c
│   │   │   └── tmp
│   │   └── CompilerIdCXX
│   │       ├── a.out
│   │       ├── CMakeCXXCompilerId.cpp
│   │       └── tmp
│   ├── cmake.check_cache
│   ├── CMakeDirectoryInformation.cmake
│   ├── CMakeOutput.log
│   ├── CMakeTmp
│   ├── hello.dir
│   │   ├── build.make
│   │   ├── cmake_clean.cmake
│   │   ├── CXX.includecache
│   │   ├── DependInfo.cmake
│   │   ├── depend.internal
│   │   ├── depend.make
│   │   ├── flags.make
│   │   ├── link.txt
│   │   ├── main.cpp.o
│   │   └── progress.make
│   ├── Makefile2
│   ├── Makefile.cmake
│   ├── progress.marks
│   └── TargetDirectories.txt
├── cmake_install.cmake
├── CMakeLists.txt //
├── main.cpp //
└── Makefile

在执行 make 操作,来编译生成的 Makefile

output

[rwbyguo@VM-218-157-centos cmake_test]$make
Scanning dependencies of target hello
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

会得到一个编译后的可执行文件 hello

.
| ...
├── hello // new
├── main.cpp
└── Makefile
    
[rwbyguo@VM-218-157-centos cmake_test]$./hello 
hello world

# 语法介绍

# project 关键字

指定工程名和支持语言,默认情况下支持所有语言 project(${project_name} [language])

project(CMakeTest)  		# 支持所有语言
project(CMakeTest CXX)		# 支持 c++
project(CMakeTest C CXX)	# 支持 c++ 和 c

该指令会隐式创建两个变量 ${project_name}_BINARY_DIR${project_name}_SOURCE_DIR

另外还有两个预定义变量值和它们一致 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR

# set 关键字

声明变量,并且支持多个

set(SRC_LIST main.cpp m1.cpp m2.cpp)

# message 关键字

向终端输出用户自定义的信息,类似 print。包含多种类型的信息,具体说明参考官方文档

  • SEND_ERROR:产生错误,会继续运行 CMakeLists.txt 内的指令,但是不会构建
  • STATUS:输出前缀带有「— —」的信息
  • FAIL_ERROR:终止运行 CMakeLists.txt 内的指令,且停止构建
  • WARNING:警告,但是会继续运行和构建

# add_executable 关键字

用来生成可执行文件,包含两个参数,一个是可执行文件名,一个是源文件: add_executable(${executable_name} ${source_list})

add_executable(hello main.cpp m1.cpp m2.cpp)

# 语法的基本原则

  • 变量值的获取需要使用 ${} ,如果在 IF 语句内使用变量则可直接用变量名。
  • 指令内的参数都是用 () 包裹,参数之间可以用 ; 或者 空格 分隔。
  • 关键字不区分大小写,但是参数和变量区分大小写。

# 注意事项

  • set 指令如果参数里面带有空格的话,需要用双引号括起来 "arg 1"
  • add_executable 指令内的源文件可以不加 .cpp 后缀,但是建议还是加上

# 内部构建和外部构建

  • 上面例子就是内部构建,会获得很多临时文件
  • 外部构建可以指定生成文件都放在单独的目录下,推荐使用外部构建

外部构建介绍:

在目录下创建一个单独的目录 debugbuild ,然后在 debugbuild 执行 cmake .. 即可

[rwbyguo@VM-218-157-centos cmake_test]$cd debugbuild/
[rwbyguo@VM-218-157-centos debugbuild]$cmake ..
-- The C compiler identification is GNU 7.3.1
-- The CXX compiler identification is GNU 7.3.1
-- Check for working C compiler: /usr/lib64/ccache/cc
-- Check for working C compiler: /usr/lib64/ccache/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/lib64/ccache/c++
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is BINARY dir /data/rwbyguo/cmake_test/debugbuild
-- This is SOURCE dir /data/rwbyguo/cmake_test
-- Configuring done
-- Generating done
-- Build files have been written to: /data/rwbyguo/cmake_test/debugbuild

这时候它的 BINARYSOURCE 路径就不一样了。

# 编译一个工程

# 工程目录结构

常规的 C++ 工程目录如下,需要编译哪个目录就在哪个目录下面创建一个 CMakeLists.txt:

.
├── CMakeLists.txt
├── debugbuild			//cmake 生成的文件路径
├── external			// 存放项目引用的第三方库
│   └── CMakeLists.txt
├── resource			// 存放项目资源文件
├── run
│   └── bin				// 存放项目可执行的 bin 文件
└── src					// 存放项目代码
    ├── CMakeLists.txt
    └── main.cpp

# 进阶语法

# file 关键字

文件操作命令,用法:

file(WRITE filename "message to write"... ) # 将消息写入 filename
file(APPEND filename "message to write"... ) # 将消息追加写入 filename
file(READ filename variable [LIMIT numBytes] [OFFSET offset] [HEX]) # 从 filename 读取消息到 variable
file(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> filename variable) # 计算 filename 的加密哈希到 variable
file(STRINGS filename variable [LIMIT_COUNT num] # 将解析 filename 中的 ASCII 字符串列表并将其存储在 variable 中。二进制数据 和 回车会被忽略
     [LIMIT_INPUT numBytes] [LIMIT_OUTPUT numBytes]
     [LENGTH_MINIMUM numBytes] [LENGTH_MAXIMUM numBytes]
     [NEWLINE_CONSUME] [REGEX regex]
     [NO_HEX_CONVERSION])
file(GLOB variable [RELATIVE path] [globbing expressions]...) # 生成 variable 用来存储 RELATIVE path 下所有满足 globbing expressions 的文件
file(GLOB_RECURSE variable [RELATIVE path] # 和 GLOB 类似,但不仅仅匹配文件,还匹配目录
     [FOLLOW_SYMLINKS] [globbing expressions]...)
file(RENAME <oldname> <newname>) # 移动文件或目录,操作是原子性的
file(REMOVE [file1 ...]) # 删除文件,包括子目录
file(REMOVE_RECURSE [file1 ...]) # 删除目录或文件,包括子目录
file(MAKE_DIRECTORY [directory1 directory2 ...]) # 创建目录,支持不存在父目录的创建
file(RELATIVE_PATH variable directory file) # 获取 directory 和 file 的相对路径关系并保存在 variable。
file(TO_CMAKE_PATH path result) # 将路径转为 unix 的 cmake 样式
file(TO_NATIVE_PATH path result) # 将路径转为 windows 的 cmake 样式
file(DOWNLOAD url file [INACTIVITY_TIMEOUT timeout] # 将 url 内容下载到 file
     [TIMEOUT timeout] [STATUS status] [LOG log] [SHOW_PROGRESS]
     [EXPECTED_HASH ALGO=value] [EXPECTED_MD5 sum]
     [TLS_VERIFY on|off] [TLS_CAINFO file])
file(UPLOAD filename url [INACTIVITY_TIMEOUT timeout] # 将 filename 内容上传到 url
     [TIMEOUT timeout] [STATUS status] [LOG log] [SHOW_PROGRESS])
file(TIMESTAMP filename variable [<format string>] [UTC]) # 获取 filename 修改的时间字符串到 variable
file(GENERATE OUTPUT output_file # 将 input_file | input_content 内容写入 output_file
     <INPUT input_file|CONTENT input_content>
     [CONDITION expression])

# 常见用法:

file(GLOB_RECURSE my_src *.cpp *.h) # 获取当前路径下的所有 .cpp 和 .h
add_executable(my_executable ${my_src}) # 编译出可执行文件

# macro/endmacro 关键字

定义宏,用来给其他 CMakeLists.txt 使用。

macro(<name> [arg1 [arg2 [arg3 ...]]])
	# dosomthing ...
endmacro(<name>)

# include 关键字

引用 .cmake 文件,使用其中定义的变量或者其他函数

include(<file|module> [OPTIONAL] [RESULT_VARIABLE <VAR>]
                      [NO_POLICY_SCOPE])

# 配合 macro 使用:

# xxx.cmake
macro(myfunc msg)
	message(STATUS ${msg})
endmacro(myfunc msg)
# CMakeLists.txt
include(xxx.cmake)
myfunc("this is msg")

# if/endif 关键字

条件语句:

if(expression)
	# dosomthing ...
elseif (expression)
	# dosomthing ...
endif()

# foreach/endforeach 关键字

循环语句,语法和 python 比较类似:

foreach(loop_var arg1 arg2 ...) # foreach(var IN list_xxx) | foreach(var RANGE start stop [step])
	# dosomthing ...
endforeach()

# configure_file 关键字

将一个文件复制到另一个位置,并对文件内容进行修改

configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
               
# example
configure_file(file1 /src/file1)

# math 关键字

进行数学运算的函数

math(EXPR <output variable> <math expression>)
# example
math(EXPR var "8 * 8")

# string 关键字

string(REGEX MATCH <regular_expression>		# 正则匹配字符串,返回首个匹配项
       <output variable> <input> [<input>...])
string(REGEX MATCHALL <regular_expression>	# 正则匹配字符串,尽可能多的匹配
       <output variable> <input> [<input>...])
string(REGEX REPLACE <regular_expression>	# 正则匹配字符串,尽可能多的匹配,匹配成功后进行替换
       <replace_expression> <output variable>
       <input> [<input>...])
string(REPLACE <match_string>			  # 非正则模式下尽可能多的字符串匹配和替换
       <replace_string> <output variable>
       <input> [<input>...])
string(CONCAT <output variable> [<input>...]) # 字符串拼接类似 "".join
string(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> # 计算字符串的加密哈希
       <output variable> <input>)
# 字符串比较
string(COMPARE EQUAL <string1> <string2> <output variable>)
string(COMPARE NOTEQUAL <string1> <string2> <output variable>)
string(COMPARE LESS <string1> <string2> <output variable>)
string(COMPARE GREATER <string1> <string2> <output variable>)
string(ASCII <number> [<number> ...] <output variable>) # 将字符串中的数字转为 ASCII 编码
 # 字符串转换
string(CONFIGURE <string1> <output variable>
       [@ONLY] [ESCAPE_QUOTES])
string(TOUPPER <string1> <output variable>)
string(TOLOWER <string1> <output variable>)
string(LENGTH <string> <output variable>) # 获取长度
string(SUBSTRING <string> <begin> <length> <output variable>) # 字符串切片
string(STRIP <string> <output variable>) # 字符去空格和 tab
string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>] # 生成固定长度随机字符串
       [RANDOM_SEED <seed>] <output variable>)
string(FIND <string> <substring> <output variable> [REVERSE]) # 查找给定字串的下标
string(TIMESTAMP <output variable> [<format string>] [UTC]) # 输出当前时间字符串
string(MAKE_C_IDENTIFIER <input string> <output variable>) # will write a string which can be used as an identifier in C.

# option 关键字

相当于创建一个 bool 类型的变量

option(<option_variable> "help string describing option"
       [initial value])
       
# example
option(NEED_CHECK_CONFIG "是否需要检查配置" ON)
if (NEED_CHECK_CONFIG)
	# dosomething ...
endif ()

# add_definitions 关键字

搭配 option 使用,可以指定某个 option 不生效

add_definitions(-DFOO -DBAR ...)
# example
add_definitions("-DNEED_CHECK_CONFIG") # NEED_CHECK_CONFIG  on -> off

# list 关键字

比较简单,就是 list 容器和各项操作

list(LENGTH <list> <output variable>)
list(GET <list> <element index> [<element index> ...]
     <output variable>)
list(APPEND <list> [<element> ...])
list(FIND <list> <value> <output variable>)
list(INSERT <list> <element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value> [<value> ...])
list(REMOVE_AT <list> <index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)

# install 关键字

指定执行 cmake install 时运行的安装规则。官方文档

其中,一个内置变量经常被使用: CMAKE_INSTALL_PREFIX=/usr/local

# 常见安装模板

安装非目标程序(shell 脚本)

install(<FILES|PROGRAMS> files... DESTINATION <dir>
        [PERMISSIONS permissions...]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>]
        [RENAME <name>] [OPTIONAL])
# example FILES
# 把 README COPYRIGHT 两个文件安装到 ${CMAKE_INSTALL_PREFIX}/doc 下
install(FILES README COPYRIGHT DESTINATION /doc)
# example PROGRAMS
# 把 build.sh 安装到 ${CMAKE_INSTALL_PREFIX}/shell 下
install(PROGRAMS build.sh DESTINATION /shell)

安装一个或多个目录内容:

install(DIRECTORY dirs... DESTINATION <dir>
        [FILE_PERMISSIONS permissions...]
        [DIRECTORY_PERMISSIONS permissions...]
        [USE_SOURCE_PERMISSIONS] [OPTIONAL]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>] [FILES_MATCHING]
        [[PATTERN <pattern> | REGEX <regex>]
         [EXCLUDE] [PERMISSIONS permissions...]] [...])
         
# example DIRECTORY
# 把 doc 目录下的内容安装到 ${CMAKE_INSTALL_PREFIX}/doc 下
install(DIRECTORY doc/ DESTINATION /doc)

安装常用的可选项:

install(TARGETS targets... [EXPORT <export-name>]
        [RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
        [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
          PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>]
         [DESTINATION <dir>]
         [PERMISSIONS permissions...]
         [CONFIGURATIONS [Debug|Release|...]]
         [COMPONENT <component>]
         [NAMELINK_COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
         [NAMELINK_ONLY|NAMELINK_SKIP]
        ] [...]
        [INCLUDES DESTINATION [<dir> ...]]
        )
# example TARGETS
# 把 arenasvr 按照 RUNTIME 模式安装到 ${CMAKE_INSTALL_PREFIX}/as/bin 下
install(TARGETS arenasvr RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/as/bin)

# TARGETS

${targets} 用来指定需要安装的源文件

# EXPORT

指定安装后的文件名,默认为 $

# Target Type

指定 targets 的类型,如果 DESTINATION 没有设置情况下,不同类型的默认安装路径由 GNUInstallDirs Variable 决定

Target Type类型说明GNUInstallDirs VariableBuilt-In Default
RUNTIME可执行文件 | DLLs${CMAKE_INSTALL_BINDIR}bin
LIBRARY共享库${CMAKE_INSTALL_LIBDIR}lib
ARCHIVE静态库 | .lib | .dll${CMAKE_INSTALL_LIBDIR}lib
FILE_SET (type HEADERS )文件集,会保留目录结构进行安装${CMAKE_INSTALL_INCLUDEDIR}include

# DESTINATION

指定安装目录,相对路径默认以 CMAKE_INSTALL_PREFIX 为根目录

# PERMISSIONS

指定安装文件的权限

# CONFIGURATIONS

指定安装构建的规则:Debug | Release

# 静态库和动态库的构建

# 两者区别

  • 静态库的扩展名一般为 .a 或 .lib;动态库的扩展名一般为 .so 或 .dll
  • 静态库在编译是会直接整合到目标程序中,目标程序在编译完成后可独立运行
  • 动态链接库在编译时不会放到连接的目标程序中,目标程序在运行过程中动态加载,因此目标程序不可独立运行

# add_library 关键字

添加共享库构建项

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 [source2 ...])
# example
add_library(lib_func STATIC lib_func.cpp)
# 构建时会得到 lib${name}.xx 的库文件,例如这里对 lib_func 的构建结果为 liblib_func.a
关键字作用
STATIC生成静态库, .a 或 .lib
SHARED生成动态库, .so 或 .dll
MODULE生成动态库,可以在运行过程中通过 dlopen 动态加载, .so 或 .dll

在根目录下新建一个 lib 文件,并且写个简单的函数:

.
├── CMakeLists.txt
├── lib
│   ├── CMakeLists.txt
│   ├── lib.cpp
│   └── lib.h
└── src
    ├── CMakeLists.txt
    └── main.cpp

lib/CMakeLists.txt

add_library(lib_func STATIC lib.cpp)

src/CMakeLists.txt

add_executable(hello main.cpp)

CMakeLists.txt

project(ROOT)
add_subdirectory(lib bin/lib)
add_subdirectory(src bin/src)

# add_subdirectory 关键字

添加需要编译的子目录,可以指定编译文件的路径

add_subdirectory(source_dir [binary_dir]
                 [EXCLUDE_FROM_ALL])
                 
# example 编译 src 下面的 CMakeLists.txt 并把结果输出到 ${PROJECT_SOURCE_DIR}/bin/src 下
add_subdirectory(src bin/src)

# include_directories 关键字

添加外部库,可以避免在引用外部库头文件的时候,提示找不到头文件

/data/rwbyguo/cmake_test/src/main.cpp:2:10: 致命错误:lib.h:没有那个文件或目录
 #include "lib.h"
          ^~~~~~~
编译中断。

用法:

include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
# example
include_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(hello main.cpp)

为可执行程序添加链接库,可以避免在使用动态库中函数的时候找不到对应的实现

CMakeFiles/hello.dir/main.o:在函数‘main’中:
main.cpp:(.text+0x5):对‘lib_func()’未定义的引用
collect2: 错误:ld 返回 1
make[2]: *** [bin/src/hello] 错误 1
make[1]: *** [bin/src/CMakeFiles/hello.dir/all] 错误 2
make: *** [all] 错误 2

添加需要连接的共享库:

target_link_libraries(<target> [item1 [item2 [...]]]
                      [[debug|optimized|general] <item>] ...)
# example 记得 target_link_libraries 需要放在 add_executable 后面
include_directories(${CMAKE_SOURCE_DIR}/lib)
set(LIB_FUNC_PATH ${CMAKE_SOURCE_DIR}/debugbuild/bin/lib) #/data/rwbyguo/cmake_test/debugbuild/bin/lib
add_executable(hello main.cpp)
target_link_libraries(hello ${LIB_FUNC_PATH}/liblib_func.a)

添加非标准的共享库搜索路径,可以简化链接库时的路径设置,搭配 target_link_libraries 使用

link_directories(directory1 directory2 ...)
# example
include_directories(${CMAKE_SOURCE_DIR}/lib)
set(LIB_FUNC_PATH ${CMAKE_SOURCE_DIR}/debugbuild/bin/lib) #/data/rwbyguo/cmake_test/debugbuild/bin/lib
link_directories(${LIB_FUNC_PATH})
add_executable(hello main.cpp)
target_link_libraries(hello liblib_func.a)

连接库,可以不需要指定 target,相等于 target_link_libraries 提前版本。

link_libraries(library1 <debug | optimized> library2 ...)
# example
include_directories(${CMAKE_SOURCE_DIR}/lib)
set(LIB_FUNC_PATH ${CMAKE_SOURCE_DIR}/debugbuild/bin/lib) #/data/rwbyguo/cmake_test/debugbuild/bin/lib
link_libraries(${LIB_FUNC_PATH}/liblib_func.a)
add_executable(hello main.cpp)

# find_package 关键字

查找并加载外部包,支持两种搜索方式:

  • Module mode:CMake 先从 CMAKE_MODULE_PATH 搜索一个名为 的文件 Find<PackageName>.cmake
  • Config mode:CMake 会搜索一个名为 <lowercasePackageName>-config.cmake | <PackageName>Config.cmake 的文件。查找流程较为复杂,见官方文档
find_package(<package> [version] [EXACT] [QUIET]
             [REQUIRED] [[COMPONENTS] [components...]]
             [CONFIG|NO_MODULE]
             [NO_POLICY_SCOPE]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ... ]]
             [PATHS path1 [path2 ... ]]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [NO_DEFAULT_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_CMAKE_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH]
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])
              
# example 官方提供了 protobuf 的配置 https://cmake.org/cmake/help/latest/module/FindProtobuf.html
#protobuf
set(PROTOBUF_DIR ${CMAKE_SOURCE_DIR}/external/protobuf)
set(PROTOBUF_INCLUDE_DIR ${PROTOBUF_DIR}/include)
set(PROTOBUF_LIBRARY_DIR ${PROTOBUF_DIR}/lib)
set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_DIR}/bin/protoc)
if(MSVC)
    set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_DIR}/bin/protoc.exe)
endif(MSVC)
set(PROTOBUF_LIBRARY ${PROTOBUF_LIBRARY_DIR}/libprotobuf.a)
set(PROTOBUF_LIBRARY_DEBUG ${PROTOBUF_LIBRARY_DIR}/libprotobuf.a)
set(PROTOBUF_IMPORT_DIRS)
find_package(Protobuf REQUIRED)

# EXACT

要求 version 版本匹配

# QUIRT

如果引入外部包失败,不提示消息

# REQUIRED

如果引入外部包失败,停止编译流程,并提示错误消息

# 参考链接

  • cmake 官方文档
更新于 阅读次数

请我[恰饭]~( ̄▽ ̄)~*

鑫酱(●'◡'●) 微信支付

微信支付

鑫酱(●'◡'●) 支付宝

支付宝