CMake极速入门

news/2024/10/15 18:29:56

引言

还在手写晦涩难懂的Makefile文件吗?现如今,主流的c++项目都采取CMake作为项目构建工具,CMake可以跨平台运行,而且语法相对Makefile而言直观很多,是时候将Makefile扫进垃圾堆了。

Hello, World!

首先先以单个源文件项目为讲解,新建一个main.cpp文件:

#include<iostream>using namespace std;int main(void){cout<<"Hello, World!"<<endl;
}

那么如何使用CMake编译这个文件呢?CMake所有的语句都写在一个名为CMakeLists.txt的文件下,新建这个文件:

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)#设置项目名
project(hello)#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp)

cmake_minimum_required函数的作用是限定运行的cmake版本,因为cmake脚本语言的sdk可能会有变化,老版本运行不起来。project函数就不解释了,见闻知意。add_executable函数是起到编译作用的,第一个参数是编译后的可执行程序名称(这里PROJECT_NAME是一个变量,当我们用project函数设定项目名时,会自动赋值给它,而${}是CMake中调用变量的语法),之后的参数是编译可执行文件需要的源文件依赖。
终端输入cmake .
image
可以看到,cmake正在检查系统环境,检查c,c++编译器路径,命令执行完毕后,你会发现当前目录下多了很多东西:
image
其中最关键的是Makefile文件,也就是说CMake自动将CMakeLists.txt中的语句转化为Makefile文件了,执行make命令:
image
可以看到,成功运行了,相比于手写Makefile文件,优雅许多。

目录简洁化

输入ls,查看项目当前文件:
image
可以看到,cmake生成了很多中间产物,这就把目录搞脏了,有一个约定俗成的处理方法是,新建一个build目录:
image
在build目录下执行cmake ..make
image
可以看到,这样项目目录就干净多了,编译文件和项目文件互不干涉。但是编译后的可执行文件也混在build目录中了,如果我想把可执行文件放在一个单独的目录里,该怎么做呢?

设置可执行文件输出目录

我通常喜欢把可执行文件放到项目目录下的bin文件夹,新建一个bin目录,并在CMakeLists.txt文件中添加:

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

通过设置EXECUTABLE_OUTPUT_PATH变量即可实现。

引入库文件,头文件

实际项目中不可能就一个源文件,我们还会将代码分类,组织成不同的头文件和库文件,那么CMake怎么引入呢?
假设我们现在想给Hello World程序加一个加法功能。在项目目录下,新建一个src文件夹,用于存放库文件;新建一个inc文件夹,用于存放头文件。
编写inc/add.h:

#pragma onceint add(int a,int b);

编写src/add.cpp:

#include"add.h"int add(int a,int b){return a+b;
}

当前项目结构变为:
image
再修改CMakeLists.txt文件:

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)#设置项目名
project(hello)#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

可以看到,我们添加了include_directories函数和file函数,其中include_directories函数顾名思义,就是用来指定头文件目录的。参数PROJECT_SOURCE_DIR则表示项目根目录。而file函数是用来读取文件系统的,GLOB参数代表匹配指定模式的文件(即*.cpp文件)。
然后我们再修改一下main.cpp文件,调用一下add函数:

#include<iostream>
#include"add.h"using namespace std;int main(void){cout<<"Hello, World!"<<endl;cout<<"5+10="<<add(5,10)<<endl;
}

编译,运行,成功!
image

将代码封装成库

实际开发中,为了缩短编译时间,便于代码移植等原因,通常会将代码封装成静/动态库。

静态库

静态库的封装

在CMakeLists.txt文件中加入:

add_library(add STATIC ${SRC_FILES})

其中add_library函数第一个参数是库的名字(注意不能和可执行文件名重复了)
编译:
image

设置静态库生成目录

可以从上图看到,libadd.a直接编译到build目录下了,不整洁。项目根目录下新建lib文件夹,修改CMakeLists.txt文件,加入:

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

重新编译,可以看到:
image

链接静态库

CMakeLIsts.txt文件中,加入:

#包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#链接静态库
link_libraries(add)

编译,运行,可以看到成功链接了libadd.a:
image

动态库

将上文静态库的CMakeLists.txt文件中的add_library(add STATIC ${SRC_FILES})改为add_library(add SHARED ${SRC_FILES})即可。
运行效果:
image

一些有用的设置

  • 设置C++标准(假设设为C++11):set(CMAKE_CXX_STANDARD 11)
  • 设置编译模式:SET(CMAKE_BUILD_TYPE "Debug")SET(CMAKE_BUILD_TYPE "Release")
  • 添加编译参数,比如我们想添加更多警告: add_compile_options(-Wall -Wextra -Wpedantic)

模板

直接编译二进制文件

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)#设置项目名
project(hello)#设置c++标准
set(CMAKE_CXX_STANDARD 11)SET(CMAKE_BUILD_TYPE "Release")
add_compile_options(-Wall -Wextra -Wpedantic)#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

静态库

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)#设置项目名
project(hello)#设置c++标准
set(CMAKE_CXX_STANDARD 11)SET(CMAKE_BUILD_TYPE "Release")
add_compile_options(-Wall -Wextra -Wpedantic)#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)#设置静态库
add_library(add STATIC ${SRC_FILES})#设置库的生成目录
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)#包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#链接库
link_libraries(add)

动态库

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)#设置项目名
project(hello)#设置c++标准
set(CMAKE_CXX_STANDARD 11)SET(CMAKE_BUILD_TYPE "Release")
add_compile_options(-Wall -Wextra -Wpedantic)#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)#设置动态库
add_library(add SHARED ${SRC_FILES})#设置库的生成目录
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)#包含库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#链接库
link_libraries(add)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/26470.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

《Node.js+Vue.js+MangoDB全栈开发实战》已出版

《Node.js+Vue.js+MangoDB全栈开发实战》 随书源码下载地址: 链接:https://pan.baidu.com/s/1DQYgPZLmtJCIuDXs8gub_w?pwd=1127 提取码:1127 课件下载地址: 链接:https://pan.baidu.com/s/1M36y1xu-gIUidDxw38GlBg 提取码:1988 随书目录 目 录 第1章 Node.js和TypeS…

for reading (没有那个文件或目录)en file `

001、奇怪的报错: for reading (没有那个文件或目录)en file `[sy20223040796@admin1 test]$ ls ## 测试文件及命令 test.bed test.sh [sy20223040796@admin1 test]$ cat test.bed ## 测试文件 1 5400001 5400002 1 5425001 5425002 1 …

go学习03

路由分组v1 := router.Group("/v1"){v1.POST("/login", loginEndpoint)v1.POST("/submit", submitEndpoint)v1.POST("/read", readEndpoint)}v2 := router.Group("/v2"){v2.POST("/login", loginEndpoint)v2.POST…

推荐3款程序员常用的画图工具

前言 经常看到有小伙伴在DotNetGuide技术社区微信交流群里问:有什么好用的画图工具推荐的?今天大姚给大家推荐3款程序员日常工作中常用的画图工具,大家可以根据自己的需求选择。 ProcessOn ProcessOn是一款专业强大在线作图工具,提供AI生成思维导图流程图,支持思维导图、流…

kubernetes(k8s)

应用程序部署的演变过程 在部署应用程序的方式上,主要经历了三个时代传统部署互联网早期,会直接将应用程序部署在物理机上 优点: 简单,不需要其他技术的参与 缺点: 不能为应用程序定义资源使用边界,很难合理的分配计算资源,而且程序之间容易产生影响虚拟化部署可以在一台…

Spring6 当中 Bean 的生命周期的详细解析:有五步,有七步,有十步

1. Spring6 当中 Bean 的生命周期的详细解析:有五步,有七步,有十步 @目录1. Spring6 当中 Bean 的生命周期的详细解析:有五步,有七步,有十步每博一文案1.1 什么是 Bean 的生命周期1.2 Bean 的生命周期 "五步"1.3 Bean 的生命周期 “七步”1.4 Bean 的生命周期 …

mycat启动报错Could not reserve enough space for 2097152KB object heap

mycat启动报错: 报错1:Could not reserve enough space for 2097152KB object heap 找到wrapper.conf修改内存大小为1G # Initial Java Heap Size (in MB) #wrapper.java.initmemory=3 wrapper.java.initmemory=1024# Maximum Java Heap Size (in MB) #wrapper.java.maxmemor…

26-Spring源码分析(一)

1. Spring 架构设计 Spring 框架是一个分层架构,他包含一系列的功能要素,并被分为大约 20 个模块。1.1 设计理念 Spring 是面向 Bean 的编程(BOP:Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。Bean 在 Spring 中作用就像 Object 对 OOP 的意义一样,没有…