从Makefile到CMake

从Makefile到CMake

makefile是用来编译的,而cmake是用来配置makefile的。工作中常用的是cmake,因为其相当于makefile的上层语言。

先简单学习makefile,再重点学cmake。

小知识:

文件类型阶段链接方式特性
.o编译单个编译单元,未链接
.a链接静态链接完整拷贝库代码到可执行文件,可执行文件体积大,不依赖外部库,易于分发
.so链接 + 运行动态链接运行时才加载库,可执行文件体积小,节约资源,但运行时依赖外部库

.a.so都是一组 .o 文件的集合。

gcc中,-I代表头文件路径,-L带包库文件路径,-lxxx代表链接一个库,-Dxxx代表编译时定义一个宏,相当于#define。

在 Linux/Unix 系统中,可执行文件通常没有扩展名

像**main.ofoo.o** 是直接的输入文件,链接器可以直接处理,不需要额外的选项。

-shared 选项是用来改变链接器的工作模式的,告诉它这次不是要生成可执行文件,而是要生成一个共享库。

生成静态库需要

# `ar` 命令的参数含义:
# - `r`:replace,如果库中已存在同名目标文件,则替换它。
# - `c`:create,如果库文件不存在,则创建它。
# - `s`:创建索引,这对链接器查找符号至关重要。
ar rcs libmy_lib.a foo.o bar.o

一、Makefile

1、基础语法

.PHONY:daxiang

daxiang:
	echo "daxiang"
all darren:
	echo "Hello World"

若执行make,则会运行第一个目标,即daxiang。因为make命令默认执行第一个在文件中遇到的、非 .PHONY 的目标

若执行make all darren,则会运行第二个目标,即hello world。

.PHONY: 告诉make执行的时候不要把daxiang当成一个文件目标,不要生成该目标,并强制执行,不过daxiang是否已经uptodate。

simple: main.o foo.o
	gcc -o simple main.o foo.o
main.o: main.c
	gcc -o main.o -c main.c
foo.o: foo.c
	gcc -o foo.o -c foo.c
clean:
	rm simple main.o foo.o

simple后面的便是其依赖,其先会去生成依赖文件,且按照依赖顺序,如先生成main.o,再生成foo.o。然后再生成simple可执行文件。

而且每次并不是一定回去执行所有命令,当make执行时发现生成的目标文件的日期要比其依赖要晚,说明目标文件是uptodate的,也就不会去执行。

所以才需要.PHONY:来保证一些命令一定会被执行。

2、变量

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o

$(EXE): $(OBJS)
	$(CC) -o $(EXE) $(OBJS)
main.o: main.c
	$(CC) -o main.o -c main.c
foo.o: foo.c
	$(CC) -o foo.o -c foo.c
clean:
	$(RM) $(EXE) $(OBJS)

这里用$()来代表变量。

还有一种叫做自动变量

◼ ** @ ∗ ∗ 用于表示一个规则中的目标。当我们的一个规则中有多个目标时, ∗ ∗ @**用于表示一个规则中的目标。当我们的一个规则中有多个目标时,** @用于表示一个规则中的目标。当我们的一个规则中有多个目标时,@**所指的是其中任何造成命令被运行的目标。

◼ **$^**则表示的是规则中的所有先择条件(依赖)。

◼ **$<**表示的是规则中的第一个先决条件(依赖)。

wildcard 是通配符函数,通过它可以得到我们所需的文件形式:$(wildcard pattern)

patsubst 函数是用来进行字符串替换的,其形式是:$(patsubst pattern, replacement, text)。

下面的代码便通过自动变量来实现不管目录下加入多少文件都可以生成对应的.o文件并生成可执行文件:

在这里插入图片描述

%.o: %.c是模式规则,当需要.o文件时,make会找到并执行此规则。

二、CMake

CMake相当于对Makefile进行了二次开发,只要目录里面有一个CMakelists.txt,便可执行cmake。一般来说是新建一个build目录,并在build目录中执行cmake …,这样中间代码以及makefile便会在build中被整理。然后执行make便可进行编译,make install便可以将可执行文件安装在电脑上供全局使用。

# 1. 声明 CMake 版本要求
# 必须放在文件的最开始
cmake_minimum_required(VERSION 3.10)

# 2. 设置项目信息
# 定义项目名称、版本等元数据
project(my_project VERSION 1.0)

# 3. 设置编译选项(可选)
# 设置一些全局的编译标志
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_POSITION_INDEPENDENT_CODE True)

# 4. 添加子目录
# 如果项目有子目录,并且子目录有自己的CMakeLists.txt,在这里添加
# add_subdirectory(src)
# add_subdirectory(tests)

# 5. 添加头文件搜索路径(可选)
# 如果你的头文件不在项目根目录,需要指定搜索路径
# include_directories(include)

# 6. 添加可执行文件或库
# 这是项目的核心部分,定义如何生成可执行文件或库
# add_executable(my_app main.cpp foo.cpp)

# 如果是库,可以是静态库或共享库
# add_library(my_lib STATIC lib.cpp)
# add_library(my_shared_lib SHARED lib.cpp)

# 7. 链接库
# 链接可执行文件或库需要的依赖库
# target_link_libraries(my_app PRIVATE my_lib pthread)

# PRIVATE:只在my_app编译时需要
# PUBLIC:my_app的下游依赖也需要
# INTERFACE:my_app的下游依赖需要,但my_app自身不需要

# 8. 安装规则(可选)
# 定义如何安装生成的文件
# install(TARGETS my_app DESTINATION bin)
# install(TARGETS my_shared_lib DESTINATION lib)
# install(DIRECTORY include/ DESTINATION include)

# 9. 配置测试(可选)
# 为项目配置测试
# enable_testing()
# add_test(NAME my_test COMMAND my_app)

1、cmake_minimum_required:cmake版本名称。

2、project:项目名,和生成的文件名无关。

3、set:设置变量。

4、add_subdirectory:添加子目录,会执行子目录的makefile。

5、include_directories、link_libraries:添加头文件,库文件搜索路径,但是因为可能会遇到冲突问题,所以一般绑定在一个target上,如

target_include_directories、target_link_libraries

但是运用target_include_directories、target_link_libraries时要保证得先有目标才能链接。

当你使用 target_link_libraries 并只提供库的名称(例如 my_lib),而不指定全名(如 libmy_lib.alibmy_lib.so)时,CMake 会根据以下顺序来寻找库文件:

  1. 优先寻找动态库(.so 文件): 链接器默认会先在指定的库搜索路径(通过 -L 或 CMake 的 link_directories 等设置)中寻找 libmy_lib.so。如果找到,它就会链接这个动态库。
  2. 如果找不到动态库,再寻找静态库(.a 文件): 如果链接器在所有搜索路径中都没有找到动态库,它才会转而寻找同名的静态库 libmy_lib.a

除非:

target_link_libraries(my_app PRIVATE libmy_lib.a)

PRIVATE 的意思是:这个库只对当前的目标可见,而它的下游依赖(即链接了当前目标的其它目标)则无法看到它。如果另一个目标 my_other_app 链接了 my_app(例如 target_link_libraries(my_other_app PRIVATE my_app)),它不会自动获得 my_lib 的链接信息。

6、add_executable、add_library:用来生成可执行文件或者库文件。

ADD_LIBRARY( hello_shared **SHARED** libHelloSLAM.cpp ) 生成动态库
ADD_LIBRARY( hello_shared **STATIC** libHelloSLAM.cpp ) 生成静态库

7、install:用来安装到电脑上,把不同类型文件/文件夹安装到不同的系统目录中。

第三项一般是DESTINATION

# 可执行文件、静态库或动态库
install(TARGETS <target>... [...])
# 安装普通文件
install({FILES | PROGRAMS} <file>... [...])
# 安装目录及其内容
install(DIRECTORY <dir>... [...])
# 安装阶段执行一个 CMake 脚本
install(SCRIPT <file> [...])
# 安装阶段执行一段内联的 CMake 代码
install(CODE <code> [...])
# 安装并导出项目目标信息,其他项目可以通过 find_package() 来使用你的库
# 如果只是install target,则没有该项目的其他信息,如果别的目标文件生成文件要用到该
# 库,可能还需要link、include 别的东西,而如果使用export,则可以直接使用
# find_package()试用库。
install(EXPORT <export-name> [...])

8、AUX_SOURCE_DIRECTORY:找到路径下所有源文件并用variable代表它们,通常用于文件很多的情况:aux_source_directory(<dir> <variable>)

9、可用**-D命令**来定义cmake中的变量(包括内置变量)

cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..

常见的内置类型:

PROJECT_BINARY_DIR:包含顶层 CMakeLists.txt 文件的目录

PROJECT_SOURCE_DIR:CMake 正在生成构建文件的目录,如build目录

CMAKE_CURRENT_SOURCE_DIR:当前正在处理的 CMakeLists.txt 文件所在的目录

CMAKE_INSTALL_PREFIX:项目的安装根目录,在 Linux 上它的值通常是 /usr/local

LIBRARY_OUTPUT_PATH:所有库文件(动态库和静态库)的输出目录

EXECUTABLE_OUTPUT_PATH:所有可执行文件的输出目录

10、find_package:有模块模式以及配置模式,先尝试配置模式,然后失败了再尝试模块模式

(1)模块模式就是通过模块路径 (${CMAKE_MODULE_PATH}) 中寻找一个名为 Find<PackageName>.cmake 的文件。比较老的方法

(2)新方法就是刚才介绍的install(EXPORT …),find_package() 会在系统的标准安装路径 (/usr/local/, /usr/ 等) 中寻找一个名为 <PackageName>-config.cmake<PackageName>Config.cmake 的文件。

find_package(OpenSSL QUIET)

使用 QUIET 选项后,即使 find_package() 没能找到 OpenSSL 库,它也不会在终端打印任何警告信息。这个命令只会默默地将 OpenSSL_FOUND 变量设置为 FALSE

REQUIRED:如果找不到指定的包,CMake 会立即报错并停止配置过程。

COMPONENTS:指定一个包中的具体组件。

find_package(Boost REQUIRED COMPONENTS system filesystem)

CONFIGMODULE:显式地告诉 find_package() 应该使用配置模式还是模块模式来查找。

11、add_definitions

add_definitions() 命令的作用,就相当于在编译命令中添加了 -D 选项。

add_definitions(-DDEBUG)

尽管 add_definitions() 依然可用,但现代 CMake 推荐使用 target_compile_definitions() 命令,因为它提供了更好的可见性和作用域控制

12、同时生成静态库和动态库时要注意,由于系统在生成时无法区分同名的静态或者动态库,所以要先加名称再删除:

target_link_libraries(${CMAKE_PROJECT_NAME}_shared ${LINK_LIB_LIST})
set_target_properties(${CMAKE_PROJECT_NAME}_shared PROPERTIES OUTPUT_NAME "${CMAKE_PROJECT_NAME}")
add_library(${CMAKE_PROJECT_NAME}_static STATIC ${SRC_LIST})
set_target_properties(${CMAKE_PROJECT_NAME}_static PROPERTIES OUTPUT_NAME "${CMAKE_PROJECT_NAME}")

13、FILES_MATCHING PATTERNinstall(DIRECTORY) 命令中的一个选项,用于筛选要安装的文件。

install(DIRECTORY src/${SUB_DIR} DESTINATION ${INSTALL_PATH_INCLUDE} FILES_MATCHING PATTERN "*.h")

14、list语法

CMake 中用于从列表中移除指定项的命令。

list(REMOVE_ITEM SRC_LIST "src/Network/Socket_ios.mm")

用于在列表的末尾追加一个或多个新项。

set(MY_LIST "a" "b")
list(APPEND MY_LIST "c" "d")

用于在列表的指定位置插入一个或多个新项。

set(MY_LIST "a" "b" "c")
list(INSERT MY_LIST 1 "x" "y") # 索引从0开始

15、编译选项

add_compile_options(-Wno-deprecated-declarations):这条命令告诉编译器,忽略所有关于已弃用(deprecated)声明的警告。当你的代码使用了某些在未来版本中可能被移除或替换的函数、类或变量时,编译器通常会发出这类警告。

add_compile_options(-Wno-predefined-identifier-outside-function):这条命令告诉编译器,忽略关于在函数外部使用预定义标识符的警告。这通常与某些特定的编译器扩展或代码风格有关。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值