Cmake 简介
Cmake 是什么
1、Cmake是高级编译配置工具。当多个人要用不同的语言或者编译器开发一个项目,最终要输出一个可执行文件或共享库,这时候Cmake 的作用就凸显出来了
2、Cmake的所有操作都是通过编译CMakeLists.txt
所写内容来完成的
3、学习Cmake的目的,是为了将来处理大型 C/C++/Java 项目做准备,其也是一种生成 Makefile 的工具
Cmake 的安装
- 之前的VScode 配置 Clang 和 Cmake 环境文章中写过 Cmake 的安装与环境变量配置,请参考此文章
引入:Cmake 编译 hello_world 程序
首先项目文件夹内中写一个名为
main.cpp
的hello_world源码// main.cpp #include <iostream> using namespace std; int main() { cout << "hello world"; return 0; }
使用Cmake编译,需要在项目文件夹创建
CMakeLists.txt
文件(注意区分大小写)# CMakeLists.txt project(HELLO CXX) set(SRC_LIST test.cpp) message(STATUS "This is BINARY_DIR " ${PROJECT_BINARY_DIR}) message(STATUS "This is SOURCE_DIR " ${PROJECT_SOURCE_DIR}) add_executable(hello ${SRC_LIST})
在当前目录打开终端,输入
cmake .
,使用 Cmake 进行编译,如果没有报错,会生成很多文件,再使用make
编译生成可执行文件
Cmake 语法介绍
project
命令说明
1、
project
可以用来指定工程的名字和支持的语言,默认支持所有语言
2、project(HELLO)
:指定工程名为HELLO,并支持所有语言
3、project(HELLO CXX)
:指定工程名为HELLO,并指定语言为 C++
4、project(HELLO C CXX)
:指定工程名为HELLO,并指定语言为 C 和 C++两个隐式变量
1、该指定隐式声明了两个 Cmake 变量
2、<projectname>_BINARY_DIR
,上例中为HELLO_BINARY_DIR
3、<projectname>_SOURCE_DIR
,上例中为HELLO_SOURCE_DIR
4、当前该项目这两个变量都指向当前工作目录,后续会讲外部编译
5、如果更改了工程名,这两个变量名也会更改。为了解决这个问题,可以使用Cmake 预定义的两个变量:PROJECT_BINARY_DIR
和PROJECT_SOURCE_DIR
set
命令1、
set
可以用来显式指定变量
2、set(SRC_LIST main.cpp)
:指定SRC_LIST变量包含main.cpp
3、set(SRC_LIST main.cpp t1.cpp t2.cpp)
:指定SRC_LIST变量包含后面的所有内容
4、set(SRC_LIST "main.cpp")
:可以在文件名外加""
,如果源文件名包含空格,则必须用""
包括message
命令1、
message
用于向终端输出用户自定义的信息,主要包含三种信息
2、SEND_ERROR:产生错误,生成过程被跳过
3、STATUS:输出前缀为--
的信息
4、FATAL_ERROR:立即终止所有 Cmake 过程ADD_EXECUTABLE
命令1、
ADD_EXECUTABLE
用于生成可执行文件
2、ADD_EXECUTABLE(hello ${SRC_LIST})
:生成的可执行文件名为hello,源文件来自读取SRC_LIST变量中的内容语法的基本原则
1、变量使用
${变量名}
方式取值,但在IF 控制语句中是直接使用变量名
2、命令的参数使用()
括起,参数之间使用空格或分号隔开
3、命令不区分大小写,但参数和变量区分大小写
内部构建与外部构建
上面的例子实际上就是内部构建,其产生的临时文件特别多,不方便清理
外部构建,就会把生成的临时文件放在build 文件夹,强烈推荐使用外部构建
使用外部构建
1、建立一个build 目录,可以在任何地方,推荐在项目当前目录下
2、进入build 目录,运行cmake ..
(..
表示上级目录,仅适用于 build 在当前目录下的情况),当然也可以写CMakeLists.txt
所在的绝对路径
3、现在,生成的文件都在build 目录下了,在build 目录中使用make
来构建工程注意外部构建的两个变量
1、
HELLO_BINARY_DIR
:是编译路经,即编译时build 文件夹的路径
2、HELLO_SOURCE_DIR
:是工程路径,仍然是之前的工程路径
构建一个工程的规则与操作
一个工程的开发和管理通常有以下规则:
1、为工程添加一个子目录 src,用于存放工程源码
2、添加一个子目录 doc,用来放置这个工程的文档hello.txt
3、在工程目录添加文本文件:COPYRIGHT
版权文件、README
用户说明
4、在工程目录添加runhello.sh
脚本,用来调用 hello 二进制文件
5、将构建后的目标文件放入构建目录的bin 子目录
6、将doc 目录的内容以及COPYRIGHT
、README
安装(拷贝)到/usr/share/doc/cmake
(此为 linux 路径)将目标文件放入构建目录的 bin 子目录
创建一个src 目录,将源码移入src。注意工程的每个目录下都有要
CMakeLists.txt
,所以src下也需要重写
CMakelists.txt
目录树结构: . |---build |---CMakeLists.txt |---src |---CMakeLists.txt |---main.cpp
# 根目录CMakeLists.txt project(HELLO) add_subdirectory(src ./bin)
# src目录CMakelists.txt add_executable(hello main.cpp)
add_subdirectory
命令1、完整参数:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
2、该命令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置,写在最外层的CMakeLists.txt
来首先告知
3、EXCLUDE_FROM_ALL
函数是将写的目录从编译中排除,如程序中的 example
4、add_subdirectory(src bin)
:将src 目录加入工程并指定编译输出的路径为 bin 目录。如果不指定 bin 目录,则所有编译结果都会存放在 src 目录中更改二进制的保存路径
1、可以使用
set
命令重新定义EXECUTABlE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
变量,来指定最终的目标二进制的位置
2、set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
3、set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
使用 CMake 来进行安装
安装的命令
1、一种是从代码编译后直接
make install
安装
2、一种是打包时的指定目录安装。两种指定目录的方法,make install DESTDIR=/tmp/test
或./configure -prefix=/usr
如何安装 hello world
1、使用 Cmake 一个新的命令:
install
,install
的安装可以包括二进制、动态库、静态库以及文件、目录、脚本等
2、使用 Cmake 一个新的变量:CMAKE_INSTALL_PREFIX
,该变量用于指定安装的路径目录树结构: . |---build |---CMakeLists.txt |---COPYRIGHT |---README |---doc |---hello.txt |---runhello.sh |---src |---CMakeLists.txt |---main.cpp
安装文件
COPYRIGHT
和README
1、在根目录
CMakeLists.txt
中添加:install(FILES COPYRIGHT README DESTINATION share/doc/cmake)
2、FILES
:安装的是文件
3、DESTINATION
:写绝对路径。也可以写相对路径,但相对路径的实际路径是${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>
4、CMAKE_INSTALL_PREFIX
默认是在/usr/local/
(此为 linux 路径,windows 默认C:\Program Files (x86)\
),在cmake时使用cmake -DCMAKE_INSTALL_PREFIX=/usr
可以指定该变量的路径,也可以使用set
命令更改变量地址安装脚本
runhello.sh
1、在根目录
CMakeLists.txt
中添加:install(PROGRAMS runhello.sh DESTINATION bin)
2、PROGRAMS
:非目标文件的可执行程序的安装(比如脚本)安装目录 doc
1、可以通过在 doc 目录下创建
CMakeLists.txt
,在其中安装目录下的文件
2、也可以在根目录CMakeLists.txt
中添加:install(DIRECTORY doc/ DESTINATION share/doc/cmake)
3、DIRECTORY
:安装的是目录。后面连接的是所在目录的相对路径
4、注意,doc
和doc/
差别很大。目录以/
结尾,将这个目录中的内容安装到指定目录;目录不以/
结尾,则会将这个目录安装到指定目录补充:安装二进制、动态库、静态库
1、安装上述都使用
TARGETS
说明,如install(TARGETS test.exe DESTINATION bin)
2、可以用ARCHIVE
特指静态库、LIBRARY
特指动态库、RUNTIME
特指二进制
3、可以通过特指分别指定安装路径,如install(TARGETS test.so test.exe LIBRARY DESTINATION lib RUNTIME DESTINATION bin)
安装过程
cmake make make install
Cmake 构建动态库
静态库与动态库的区别
1、静态库的拓展名一般为
.a
或.lib
,动态库的拓展名一般为.so
或.dll
2、静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
3、动态库在编译时不会链接到目标程序中,即可执行文件无法单独运行构建实例
目前目录中的内容
目录树结构: · |---build |---CMakeLists.txt |---lib |---CMakeLists.txt |---hello.hpp |---hello.cpp
// hello.hpp #ifndef HELLO_HPP #define HELLO_HPP void hellofunc(); #endif
// hello.cpp #include "hello.hpp" #include <iostream> void hellofunc() { std::cout << "hello world"; }
# 根目录CMakeLists.txt project(HELLO) add_subdirectory(lib ./bin)
# lib中的CMakeLists.txt set(LIBHELLO_SRC hello.cpp) add_library(hello SHARED ${LIBHELLO_SRC})
add_library
命令1、
add_library(hello SHARED ${LIBHELLO_SRC})
2、hello
:就是正常的库名,生成的名字前会加上 lib,最终产生的文件是libhello.so
(linux 下为.so
)
3、SHARED
:表示生成动态库,此外STATIC
表示生成静态库
4、${LIBHELLO_SRC}
:源文件
重命名和安装动态库及使用
同时构建静态和动态库
错误理解
# 如果用这种方式,两种库名字是一样的,只会构建一个动态库,不会构建出静态库,尽管静态库后缀是.a add_library(hello SHARED ${LIBHELLO_SRC}) add_library(hello STATIC ${LIBHELLO_SRC}) # 修改静态库的名字,这样是可以的,但往往我们希望两种库名字相同 add_library(hello SHARED ${LIBHELLO_SRC}) add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties
命令1、
set_target_properties
可以用来设置输出的名称
2、对于动态库,还可以用来指定动态库版本和API 版本
3、注意命令只是更改了输出的名称,在Cmake里操作仍然使用原始名称# lib中的CMakeLists.txt set(LIBHELLO_SRC hello.cpp) # 创建静态库名为 hello_static add_library(hello_static STATIC ${LIBHELLO_SRC}) # 重命名静态库名称为 hello set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello") # Cmake 在构建一个新的 target 时,会尝试清理掉其他使用这个名字的库,因此在下面构建动态库 libhello.so 时,就会清理掉 libhello.a,所以需要添加该设置(较新版本的 CMake 可以不用) set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUTS true) add_library(hello SHARED ${LIBHELLO_SRC})
安装共享库和头文件
本例我们要将
hello.so
动态库安装到/lib
目录,将hello.hpp
头文件安装到/include/hello
目录(下方内容继续写入lib
中的CMakeLists.txt
中)# 文件放到该目录下 install(FILES hello.hpp DESTINATION include/hello) # 二进制、静态库、动态库安装都使用 TARGETS # ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指二进制 install(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
使用头文件即动态链接库
找不到头文件
1、使用
include_directories
命令可以向工程添加多个特定的头文件搜索路径,路径之间用空格隔开
2、在CMakeLists.txt
中写入:include_directories(路径1 路径2)
找不到引用函数
1、方法一:使用
link_directories
命令可以添加非标准的动态库搜索路径
2、指定第三方库所在的路径:link_directories(路径1 路径2)
3、方法二:使用target_link_libraries
命令可以添加需要链接的动态库
4、用这种方法只要给出动态库的名字就行(这种方法对静态库也通用)
5、注意要写在executable
命令生成可执行文件后:target_link_libraries(可执行文件名 动态库名)
补充
cmake_minimum_required
命令1、
cmake_minimum_required(VERSION 2.7)
2、指定 Cmake 最低版本,如上指定 Cmake 不能低于 2.7 版本find_package
命令1、
find_package(imgui [REQUIRED])
2、在计算机中查找第三方库,首先应确保安装好了对应的库
3、REQUIRED
表示这个库是必须的,没有则会报错file(GLOB)
命令1、
file(GLOB SRC_FILES "${PROJECT_SOURCE_DIR}/src/*.cpp" "${PROJECT_SOURCE_DIR}/src/*.hpp")
2、通过通配符可以便捷引入源码文件,注意双引号${CMAKE_PROJECT_NAME}
关键字:替换为project()
指定的工程名target_compile_features
命令1、
target_compile_features(${CMAKE_PROJECT_NAME} xcc_std_17)
2、打开对C++17 标准的支持C++第三方库的安装
1、可以直接下载源码,然后手动构建并指定 Cmake 库的路径
2、对于 Linux 和 Mac,可以直接使用包管理工具安装
3、使用微软开源工具 Vcpkg 安装,类似于 python 的 pip,github 地址。只需要调用 vcpkg 安装第三方库,然后在 Cmake 构建时指定 vcpkg 工具链即可。命令行只需要额外传递一个参数:-DCMAKE_TOOLCHAIN_FILE=<vcpkg安装路径>/scripts/buildsystems/vcpkg.cmake
,如果用的是VScode 插件,只需要在设置Configure Settings中添加:"CMAKE_TOOLCHAIN_FILE": "路径"
即可
如果较为紧急,建议添加微信或QQ,并注明来意
评论系统可能加载较慢,请耐心等待或刷新重试