CMake 学习

sunrose 发布于 2024-08-04 607 次阅读


CMake基础学习:

爱编程的大丙Cmake教学

这个up主已经讲的非常详细了。这是是对他的一个简化与补充(真的有补充吗?时间够的话,是有的)

1 什么是CMake,为什么要用CMake

我们知道从源文件到可执行文件要经过编译链接等步骤。

简单来说,分为预处理,编译,汇编,链接四步。

当文件数量少时,可以通过命令行编译

gcc *.cpp -o main

但是大型工程则不能使用,因为命令会特别长,不易维护,容易出错。

解决方案有两个。一个是写makefile文件,本质是若干条指令,这些指令定义了如何编译和链接程序。然后通过批处理指令make完成构建。

另外一个则是编写CMakeLists.txt文件 通过cmake指令可以自动化生成Makefile和工程文件,然后与第一条一样,直接make完成构建

而Makefile文件并不好写,感兴趣的同学可以把下面的文件复制到vscode上自己跑一下。命令为

make 
./hello
# Makefile

# Compiler
CC = gcc

# Compiler flags
CFLAGS = -Wall -g

# Target executable
TARGET = hello

# Source files
SRCS = hello.c

# Object files
OBJS = $(SRCS:.c=.o)

# Default rule
all: $(TARGET)

# Link the target executable
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)

# Compile source files into object files
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# Clean rule to remove generated files
clean:
	rm -f $(TARGET) $(OBJS)

# Phony targets
.PHONY: all clean
// hello.c
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

而要完成这个任务CMakeLists.txt只需要三行

可以看到CMakeLists.txt大大提高了开发的效率。

2 CMake编写可执行文件

CMake不对大小写有特殊要求

注释

注释行

使用#

# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)

注释块

CMake 使用 #[[ ]] 形式进行块注释

#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

只有源文件

准备了一下6个文件(5个.cpp 1个头文件),bin文件夹用来存放生成的可执行文件,build文件夹存放与项目源码无关的文件。

CMakeLists.txt 基本思路

  • 最低版本
  • 工程名字
  • 头文件路径包含
  • 打包源文件
  • 生成可执行文件
#指定cmake最低版本
cmake_minimum_required(VERSION 3.0) 

#工程名字
project(sun)     

#file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)  #打包源文件
file(GLOB SRC ${CMAKE_SOURCE_DIR}/src/*.cpp)

#设置要包含的目录
include_directories(${CMAKE_SOURCE_DIR}/include)

#add_executable(可执行程序名 源文件名称)
add_executable(app  ${SRC})

add_executable(app main.cpp div.cpp sub.cpp mult.cpp)也可以 但是当文件变得很多的时候就很麻烦。所以采用file(GLOB SRC ${CMAKE_SOURCE_DIR}/src/*.cpp) 他将搜索src路径下所有cpp文件并打包给变量SRC

特殊要求

定义变量

如刚刚写到的将所有cpp文件并打包给变量SRC一样 CMakeLists.txt也是有变量的。语法为

SET(VAR [VALUE])
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)
# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)

使用变量的时候

add_executable(app  ${SRC_LIST})
等效于
add_executable(app  add.c  div.c   main.c  mult.c  sub.c)

指定使用的C++标准

C++标准对应有一宏叫做DCMAKE_CXX_STANDARD


#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)

指定输出的路径

在CMake中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH,它的值还是通过set命令进行设置:

set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)

搜索文件

如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在CMake中为我们提供了搜索文件的命令,可以使用aux_source_directory命令或者file命令。不指定文件名字,而是去搜索某个目录下的文件

aux_source_directory


在 CMake 中使用aux_source_directory 命令可以查找某个路径下的所有源文件,命令格式为:

aux_source_directory(< dir > < variable >)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)



file

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
  • GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
  • GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
  • CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径。

  1. GLOB:
    • GLOB 命令用于查找与指定模式匹配的文件路径列表。
    • 它仅在当前目录下搜索,不进入子目录进行递归搜索。
    • 例如,使用 GLOB SRC_FILES "src/*.cpp" 会匹配 src 目录下的所有 .cpp 文件,但不会进入 src 目录下的任何子目录。
  2. GLOB_RECURSE:
    • GLOB_RECURSE 命令同样用于查找文件路径列表,但它会递归地搜索所有匹配的文件,包括子目录中的文件。
    • 它允许你通过指定一个模式和目录来收集所有子目录中的匹配文件。
    • 例如,使用 GLOB_RECURSE SRC_FILES "src/*.cpp" 不仅会匹配 src 目录下的 .cpp 文件,还会进入 src 目录下的每个子目录,继续查找并匹配 .cpp 文件。
    ${CMAKE_CURRENT_SOURCE_DIR}是CMakeLists.txt所在的路径

包含头文件

在编译项目源文件的时候,很多时候都需要将源文件对应的头文件路径指定出来,这样才能保证在编译过程中编译器能够找到这些头文件,并顺利通过编译。在CMake中设置要包含的目录也很简单,通过一个命令就可以搞定了,他就是include_directories:

include_directories(headpath)

现在,请暂停一下,在读一下这串代码,看能否理解

#指定cmake最低版本
cmake_minimum_required(VERSION 3.0) 

#工程名字
project(CALC)     

#file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
file(GLOB SRC ${CMAKE_SOURCE_DIR}/src/*.cpp)

#设置要包含的目录
include_directories(${CMAKE_SOURCE_DIR}/include)

# set使用3 : 指定输出路径
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)

#add_executable(可执行程序名 源文件名称)
add_executable(app  ${SRC})

3 库文件的制作与引入

Linux 动态库后缀 .so 静态:.a 前缀均为lib

如libsun.so libsun.a

库的制作

动态库与静态库本质还是源代码 不过库是二进制形式 .cpp是文本文件形式

在Linux中,静态库名字分为三部分:lib+库名字+.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

在Linux中,动态库名字分为三部分:lib+库名字+.so,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

#静态库
add_library(库名称 STATIC 源文件1 [源文件2] ...) 
# 动态库
add_library(库名称 SHARED 源文件1 [源文件2] ...) 
# 生成动态库
add_library(sun SHARED ${SRC_LIST})
# 生成静态库
add_library(sun STATIC ${SRC_LIST})

制定库的输出路径

# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

接下来,请暂停一下,看是否可以读懂这段代码

#指定cmake最低版本
cmake_minimum_required(VERSION 3.0) 

#工程名字
project(sun)     

#file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
file(GLOB SRC ${CMAKE_SOURCE_DIR}/src/*.cpp)

#设置要包含的目录
include_directories(${CMAKE_SOURCE_DIR}/include)

# set使用2:定义c++版本
set(CMAKE_CXX_STANDARD 11)

 
set(LIBRARY_OUTPUT_PATH /mnt/c/test/cmake/My_CMakeLib/lib)

add_library(sun SHARED ${SRC})
#add_library(sun STATIC ${SRC})

链接静态库

link_libraries(<static lib> [<static lib>...])
  • 参数1:指定出要链接的静态库的名字
    • 可以是全名 libxxx.a
      也可以是掐头(lib)去尾(.a)之后的名字 xxx
  • 参数2-N:要链接的其它静态库的名字

如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:

link_directories(<lib path>)

演示工程

cmake_minimum_required(VERSION 3.0)
project(sun)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(sun)

#生成可执行文件
add_executable(app ${SRC_LIST})

链接动态库

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:指定要加载动态库的文件的名字
    • 该文件可能是一个源文件
    • 该文件可能是一个动态库文件
    • 该文件可能是一个可执行文件
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC

动态库的链接和静态库是完全不同的:

  • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
  • 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存

如果找不到动态库 与静态库用一个函数

link_directories(path)
#指定cmake最低版本
cmake_minimum_required(VERSION 3.0) 

#工程名字
project(CALC)     

#file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
file(GLOB SRC ${CMAKE_SOURCE_DIR}/*.cpp)

#设置要包含的目录
include_directories(${CMAKE_SOURCE_DIR}/include)


link_directories(${CMAKE_SOURCE_DIR}/shareLib)

add_executable(app ${SRC} )

target_link_libraries(app ${CMAKE_SOURCE_DIR}/shareLib/libsun.so)
一个努力学习的憨憨
最后更新于 2024-08-13