目录
前言
在平时的编译过程中,如果源文件比较少的时候可以选择直接使用GCC进行编译,但是当构建一个项目时,会需要同时对许多文件进行编译,此时我们使用CMake进行编译就会简单很多,最近也学习了一些CMake的知识想和大家进行一下分享。
1.什么是CMake?
CMake 是一个项目构建工具,并且是跨平台的。关于项目构建我们所熟知的还有Makefile(通过 make 命令进行项目的构建)。但是如果自己动手书写makefile,其工作量很大而且通常会依赖当前的编译平台,解决依赖关系的时候也容易出现很多错误。而CMake可以很好的解决这个问题,只需要用户进行make编译便可以自动生成相关的makefile和工程文件。
2.CMake的安装
首先需要手动进行cmake安装:
sudo apt install cmake
安装完成后,在终端输入:
cmake -version
查看cmake版本
3.CMake的基本语法
3.1 源码介绍
3.1.1 加法
#include <stdio.h> #include "head.h" int add(int a, int b) { return a+b; }
3.1.1 减法
#include <stdio.h> #include "head.h" int sub(int a, int b) { return a - b; }
3.1.2 main 函数
#include <stdio.h> #include "head.h" int main() { int a = 10; int b = 20; printf("a = %d, b = %d\n", a, b); printf("a + b = %d\n", add(a, b)); printf("a - b = %d\n", sub(a, b)); return 0; }
3.1.3 head.h头文件
#ifndef _HEAD_H #define _HEAD_H int add(int a, int b);// 加法 int sub(int a, int b);// 减法 #endif
3.2 CMake基本命令
如下介绍的基本命令不按照先后顺序介绍。
3.2.1 版本要求和项目名称声明
cmake_minimum_required(VERSION 3.15) project(calculate)
首先是声明cmake的最低版本要求(可以不要,但是最好还是加上),如果低于要求的版本会报错,并且有些命令是某些cmake版本中新加的,因此在书写版本要求时,要查看cmake的相关参考文档:CMake参考文档。
然后要声明工程的名称(必须),同时可以指定工程的版本,描述以及支持的语言等,如果这些不需要可以忽略。(注:写在最开头)
3.2.2set指令
3.2.2.1 声明C++使用标准
在编写C++程序的时候,可能会用到C++11、C++14、C++17、C++20等新特性,那么就需要在编译的时候在编译命令中制定出要使用哪个标准,C++中有一种宏可以设置。
set(DCMAKE_CXX_STANDARD 11) set(DCMAKE_CXX_STANDARD 14) set(DCMAKE_CXX_STANDARD 17)
3.2.2.2 生成可执行文件
# add_executable(可执行程序名 源文件名称) set(SRC_LIST add.c main.c sub.c) add_executable(cal ${SRC_LIST})
该命令将多个源文件生成为一个可执行文件。
3.2.2.3 定义变量
对于某个文件或者路径,有时我们要反复使用,而每次都要把名字都写出来会很麻烦,这时我们可以选择通过set指令定义一个变量,将目标字符串存到变量中。
set(var [value]) # var:变量名 # value:变量值
3.2.3 搜索指令
如果一个项目需要很多的源文件,在编写CMakeLists.txt文件时,我们不可能把项目目录中的每一个文件都写出来,那样就太麻烦了,因此我们可以通过搜索命令找出该目录中的所有源文件,并存储到一个变量中。
# 方法一 aux_source_directory(<dir> <var>) # dir:要搜索的目录 # var:将目录下的源文件存储到该变量中 # 方法二 file(GLOB 变量名 要搜索的文件路径和文件类型)
3.2.4 包含头文件
在编译项目源文件的时候,都需要将源文件对应的头文件指定出来,这样在编译时才能找到对应的头文件。
# include_directories(头文件所在路径) include_directories(${PROJECT_SOURCE_DIR}/include)
3.2.5 生成动态库或静态库
有时候我们编写的源代码不需要对其进行编译生成可执行程序,而是生成一些供第三方使用的静态库或动态库,下面介绍生成这两种库的方法。
3.2.5.1 静态库
add_library(库名称 STATIC 源文件1 [源文件2] ...) add_library(calculate STATIC $(SRC_LIST))
在Linux中,我们生成的静态库文件的形式为:lib+库名称+.a,其中STATIC表明为静态库,如果不加默认生成的是静态库。这样我们就生成了libcalculate.a静态库。
3.2.5.2 动态库
# add_library(库名称 SHARED 源文件1 [源文件2] ...) add_library(calculate SHARED ${SRC_LIST})
在Linux中,我们生成的动态库文件的形式为:lib+库名称+.so,其中SHARED表明为动态库,必须加上SHARED(否则为静态库)。这样我们就生成了libcalculate.so动态库。
3.2.6 指定生成的路径
我们生成库文件或者可执行程序时,可以给其指定生成的路径,我们一般保存在lib目录下。
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
3.2.7 链接库文件
在编写程序时,我们需要对系统提供的库文件或自己写的库文件进行链接,在cmake中也提供了相对应的指令。
3.2.7.1 链接静态库
# (注:如果所使用的库文件不是系统所提供,而是自己制作的静态库,需要将对应的静态库路径指定出来) link_directories(${LIBRARY_OUTPUT_PATH}) # 对静态库文件进行链接 link_libraries(<static lib1> [<static lib2>...])
在我们生成静态库后,如果我们将源文件和静态库进行链接,那么在生成可执行文件时会将库文件进行打包,当执行对应的可执行文件时,静态库的代码会一并加载到内存中。
3.2.7.1 链接动态库
# (注:如果所使用的库文件不是系统所提供,而是自己制作的动态库,需要将对应的动态库路径指定出来) link_directories(${LIBRARY_OUTPUT_PATH}) # 该命令一般用于动态库与可执行文件之间进行链接或者库与库之间进行链接 target_link_library(target lib1 <lib2>...)
在我们生成动态库后,如果我们将源文件和动态库进行链接,那么在生成可执行文件时不会将库文件进行打包,随后在执行对应的可执行文件时会调用所声明的库文件,需要检查所需要的动态库是否在所指定的路径中。
3.3 宏定义
在一些特定情况下,我们需要对其中某一部分程序判断是否需要执行,这时我们可以通过定义宏来进行控制。
#include <stdio.h> int main() { int a = 10; #ifdef DEBUG printf("CMake is great!\n"); #endif for(int i=0; i< a; i++) { printf("hello, CMake。\n"); } return 0; }
3.3.1 使用GCC进行宏定义
首先在程序中进行DEBUG宏进行判断。
如果在编译时宏被定义了,那么会进行日志输出。
而如果没有对宏进行定义,那么在执行可执行文件时这一段程序将不会执行。
3.3.2 在CMakeLists.txt中进行定义
在CMakeLists.txt文件中也提供了相应的宏定义。
cmake_minimum_required(VERSION 3.0) project(test) # 设置自定义宏 add_definitions(-DDEBUG) add_executable(test ./test.c)
然后进行编译,可以得到和上述一样的答案。
cmake . make ./test
4.总结
以上就是这段时间我所学习CMake的一点笔记,在此同时也看了很多其他博主的文章,希望也可以通过分享一些简单的指令帮助大家入门(当然还有很多其他的命令宏在需要时再学习)。
如果有哪些写的不对的地方,希望大家可以指正,谢谢!