Linux C_C++的编译

GCC

官方网站
GCC(GNU Compiler Collection,GNU编译程序集合)是最重要的开放源码软件。其他所有开放源码软件都在某种层次上依赖于它。甚至其他语言,例如 Python,都是由 C 语言开发的,由 GNU 编译程序编译的。

常见组件

  • c++: gcc 的一个版本,默认语言设置为 C++,而且在链接的时候自动包含标准 C++ 库。这和 g++ 一样
  • configure: GCC 源代码树根目录中的一个脚本。用于设置配置值和创建 GCC 编译程序必需的 make 程序文件
  • g++: gcc 的一个版本,默认语言设置为 C++,而且在链接的时候自动包含标准 C库。这和 c 一样
  • gcc: 该驱动程序等同于执行编译程序和连接程序以产生需要的输出
  • libgcc: 该库包含的例程被作为编译程序的一部分,是因为它们可被链接到实际的可执行程序中。它们是特殊的例程,链接到可执行程序,来执行基本的任务,例如浮点运算。这些库中的例程通常都是平台相关的
  • libstdc++: 运行时库,包括定义为标准语言一部分的所有的 C++类和函数

常见软件

  • ar: 这是一个程序,可通过从文档中增加、删除和析取文件来维护库文件。通常使用该工具是为了创建和管理连接程序使用的目标库,一般为静态库。该程序是 binutils 包的一部分 。
  • as: GNU 汇编器。实际上它是一族汇编器,因为它可以被编译或能够在各种不同平台上工作。该程序是 binutjls 包的一部分
    autoconf:产生的 shell 脚本自动配置源代码包去编译某个特定版本的 UNIX
  • gdb: GNU 调试器,可用于检查程序运行时的值和行为
    GNATS:GNU 的调试跟踪系统(GNU Bug Tracking System)。一个跟踪 GCC和其他 GNU 软件问题的在线系统
  • gprof: 该程序会监督编译程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供的配置文件来优化程序。该程序是 binutils 包的一部分
  • ld: GNU 连接程序。该程序将目标文件的集合组合成可执行程序。该程序是 binutils 包的一部分
  • libtool: 一个基本库,支持 make 程序的描述文件使用的简化共享库用法的脚本
  • make: 一个工具程序,它会读 makefile 脚本来确定程序中的哪个部分需要编译和连接,然后发布必要的命令。它读出的脚本(叫做 makefile 或 Makefile)定义了文件关系和依赖关系

C 程序编译

一般流程

源文件.c文件 -> 预编译成.i文件 -> 编译成汇编语言.s -> 汇编成.o文件 -> 链接成可执行文件

  1. 用编辑器编写源代码(文本),如.c文件
  2. 然后通过预处理器 cpp 变为修改后的源代码(文本)(导入头文件内容,删除注释等)如.i 文件。
  3. 接着编译器 ccl 将其转换为汇编程序(文本),如.s文件
  4. 汇编器 as 编译代码生成可重定位目标文件(二进制),如.o
  5. 最后链接器 **ld **链接目标代码生成可执行文件(二进制)。

举个例子
hello.c 为例

compilesystem.png

C 相关后缀文件filenamesuffixes1.png

Executable program

可执行程序
预处理

# 不会生成 .i 文件
# -E 选项告诉编译器只进行预处理操作
# -o 选项把预处理的结果输出到指定文件
gcc -E main.c 
gcc -E main.c -o helloworld.i

生成汇编语言

# -S 选项告诉编译器,进行预处理和编译成汇编语言操作
gcc -S main.c
gcc -S main.c -o xxx.s

生成目标对象文件

gcc -c main.c
gcc -c main.c -o xxx.o
# 编译多个 .c 文件,同样输出多个 .o 文件
gcc -c main.c add.c minus.c

单个文件编译为可执行程序

gcc main.c
gcc main.c -o xxx

单个xxx就是可以执行文件了,可以通过./xxx运行

多个文件编译为可执行程序

gcc main.c add.c minus.c -o exec
./exec

static library && shared library

静态库和动态库,封装功能函数

静态库

编译为可链接的目标对象文件**.o**

gcc -c [.c] -o [功能函数] 
gcc -c [.c] [.c] ...

假设有三个文件 main.cadd.cminus.c,其中后两个为功能函数。

gcc -c main.c add.c minus.c

那么编译后得到三个对象文件main.oadd.ominus.o
编译为静态库

ar -r [lib自定义库名.a] [.o] [.o] ...

将两个功能函数编为静态库

ar -r liboperation.a add.o minus.o

得到静态库liboperation.a

链接为可执行文件

gcc [.c] [.a] -o [自定义输出文件名]
gcc [.c] -o [自定义输出文件名] -l[库名] -L[库所在路径]

链接 main 函数和静态库得到可执行程序

gcc main.c liboperation.a -o exec

也可以链接三个对象文件变为可执行程序

gcc main.o add.o minus.o -o exec

动态库

二进制对象文件

gcc -c -fpic [.c/.cpp][.c/.cpp]... 

gcc -shared [.o][.o]... -o [lib自定义库名.so]

也可以直接编成库文件

gcc -shared [.o文件] [.o文件] [...] -o lib[库名].so

链接为可执行文件

gcc [.c/.cpp] -o [自定义可执行文件名]  -l[库名] -L[库路径] -Wl,-rpath=[库路径]

C++ 程序编译

C++ 相关后缀文件

fundamentalcpp.png

Executable program

预处理
和 c 类似,不过用的是 g++

#-E 选项告诉编译器只进行预处理操作
#-o 选项把预处理的结果输出到指定文件
g++ -E helloworld.c
g++ -E helloworld.c -o helloworld.i

生成汇编

#-S 选项告诉编译器,进行预处理和编译成汇编语言操作
g++ -S helloworld.c
g++ -S helloworld.c -o helloworld.s

生成二进制对象文件

g++ -c helloworld.c
g++ -c helloworld.c -o harumph.o
# 编译多个 .c 文件
g++ -c helloworld.c helloworld1.c helloworld2.c

编译单个源文件为可执行文件

g++ helloworld.c
g++ helloworld.c -o howdy

编译多个源文件为可执行文件

g++ hellomain.c sayhello.c -o hello

static library && shared library

静态库和动态库,封装功能函数

静态库

编译为可链接的目标对象文件**.o**

g++ -c [.c] -o [功能函数] 
g++ -c [.c] [.c] ...

假设有三个文件 main.cadd.cminus.c,其中后两个为功能函数。

g++ -c main.c add.c minus.c

那么编译后得到三个对象文件main.oadd.ominus.o
编译为静态库

ar -r [lib自定义库名.a] [.o] [.o] ...

将两个功能函数编为静态库

ar -r liboperation.a add.o minus.o

得到静态库liboperation.a

链接为可执行文件

g++ [.c] [.a] -o [自定义输出文件名]
gcc [.c] -o [自定义输出文件名] -l[库名] -L[库所在路径]

链接 main 函数和静态库得到可执行程序

g++ main.c liboperation.a -o exec

也可以链接三个对象文件变为可执行程序

g++ main.o add.o minus.o -o exec

动态库

二进制对象文件

g++ -c -fpic [.c/.cpp][.c/.cpp]... 

g++ -shared [.o][.o]... -o [lib自定义库名.so]

也可以直接编成库文件

g++ -shared [.o文件] [.o文件] [...] -o lib[库名].so

链接为可执行文件

g++ [.c/.cpp] -o [自定义可执行文件名]  -l[库名] -L[库路径] -Wl,-rpath=[库路径]

Intro: make && cmake

make && makefile:BTW,如果源文件非常之多,一个个编译会很麻烦,所以设计出了类似批处理的程序来批量编译源文件,这就是自动化的编译工具 make,它需要使用到一个规则文件 Makefile 来辅助自己按照规则编译。make 工具没有编译和链接功能,而是通过批处理的方式调用 Makefile 文件里存储的用户指定命令来进行编译和链接。
cmake && CMakeLists.txt:那么假设遇到很大的工程,编写 Makefile 文件也会很吃力,于是又诞生了能够自动生成makefile或者project(如Visual studio下的vcproj文件,被隐藏)的工具
cmake
,它可以读入所有源文件然后输出各种各样的makefile文件。同样需要遵守一定的规则,这就是 CMakeLists.txt 文件。值得注意的是,cmake,是可以跨平台生成对应平台的 Makefile 的。
流程如下:
image.png

make

概述

老牌的自动化构建和编译项目工具,使用名为 Makefile 的文件来定义构建规则。根据文件的依赖关系和时间戳来确定哪些文件需要重新构建,以确保构建是有效的。灵活且功能丰富,适用于各种项目,特别是在Unix/Linux环境下。
大多数IDE都集成了make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake等。
常用的命令

  1. make:运行 make 命令时,它将查找当前目录中的 Makefile 文件并执行默认的构建任务。通常,make 命令不需要参数。
  2. make <target>:运行 make 命令时,你可以指定要构建的目标。例如,make all make myprogram 将构建名为 “all” 或 “myprogram” 的目标。这些目标需要在 Makefile 中定义。
  3. make -f <filename>:使用 -f 选项可以指定一个不同的 Makefile 文件的名称,而不使用默认的 Makefile。
  4. make clean:通常,项目的 Makefile 中会包含一个 clean 目标,用于清除生成的中间文件和可执行文件。运行 make clean 将执行清理操作。
  5. make install:如果 Makefile 中包含一个 install 目标,运行 make install 可以安装项目的可执行文件或其他文件到系统中的指定位置。
  6. make uninstall:类似于 install,如果 Makefile 包含一个 uninstall 目标,运行 make uninstall 可以卸载先前安装的文件。
  7. make <variable>=<value>:你可以通过命令行覆盖 Makefile 中的变量的值。例如,make CFLAGS="-O2" 将覆盖 CFLAGS 变量的值为 “-O2”。
  8. make -n:使用-n 选项可以进行模拟构建,而不实际执行命令。这可以帮助你查看 make 将要执行的命令,但不会真正运行它们

Makefile

基础规则

基本结构
每个Makefile包含一个或者多个目标

targets : prerequisties
[tab键]command # 必须是tab键
#或者
targets : prerequisites ; command
  • target:目标文件,可以是 OjectFile,也可以是执行文件,还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。
  • prerequisite:要生成那个 target 所需要的依赖文件。
  • command:是 make 需要执行的命令

运行规则

  • make 会在当前目录下找到一个名字叫 Makefilemakefile 的文件
  • 如果找到,它会找文件中第一个目标文件(target),并把这个文件作为最终的目标文件
  • 如果 target 文件不存在,或是 target 文件依赖的 .o 文件(prerequities)的文件修改时间要比 target 这个文件新,就会执行后面所定义的命令 command 来生成 target 这个文件
  • 如果 target 依赖的 .o 文件(prerequisties)也存在,make 会在当前文件中找到 target 为 .o 文件的依赖性,如果找到,再根据那个规则生成 .o 文件

伪目标
有时候我们设置一个目标,并不是真正生成这个文件,通常用于执行特殊操作,如clean、install等。它们的目的是告诉 make 哪些操作不会产生与文件相关的输出。
避免 target 和 Makefile 同级目录下 文件/文件夹 重名的这种情况,可以使用一个特殊的标记 .PHONY 来显式地指明一个目标是 “伪目标”,向 make 说明,不管是否有这个文件/文件夹,这个目标就是 “伪目标”
.PHONY : clean
只要有这个声明,不管是否有 “clean” 文件/文件夹,要运行 “clean” 这个目标。
如,下面的clean 就是删除 myprogram,而不是真的生成clean 介个文件。

.PHONY: clean
clean:
    rm -f *.o myprogram
# 如果不想显示shell指令,可以加@进行掩盖
即 rm -f *.o myprogram

变量

声明时需要赋初值,使用时需要加$然后用()包起来
定义

cpp := src/main.cpp 
obj := objs/main.o

引用
使用$+()或者{}

cpp := src/main.cpp 
obj := objs/main.o

$(obj) : ${cpp}
	@g++ -c $(cpp) -o $(obj)

compile : $(obj)

预定义变量

  • $@: 目标(target)的完整名称
  • $<: 第一个依赖文件(prerequisties)的名称
  • $^: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件
cpp := src/main.cpp 
obj := objs/main.o

$(obj) : ${cpp}
	@g++ -c $< -o $@
	@echo $^

compile : $(obj)
.PHONY : compile

常用运算符

  1. **赋值 **

=:类似一般编程语言的赋值符号

  1. 立即赋值

:=:定义变量的时候立即求值,然后值不可更改

  1. 默认赋值

?=:如果变量已经定义就不进行任何操作,否则就进行赋值

  1. 累加

+=:就是一般编程语言里面的,右边的加到左边。

  1. 续行

\:一行写不完,可以用这个继续写

  1. 通配符

*:匹配任意字符串,用于目录名或者文件名
%:匹配任意字符串,并将字符串作为变量使用

常用函数

函数调用和变量使用类似,用$标识,语法如下,
$(fn, arguments) or ${fn, arguments}
fn:函数名
arguments:函数参数

  1. shell:调用 shell 命令

usage:$(shell <command> <arguments>)
返回 shell 命令的执行结果。

  1. subst: 字符串替换函数

usage:$(subst <old>,<new>,<text>)
返回替换后的新字符串,text 中的 old 替换为 new

  1. patsubst:模式字符串替换函数

usage:$(patsubst <pattern>,<replacement>,<text>)
从 text 中取出 pattern 替换为 replacement。可用任意长度字符串通配符 %
示例:

cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs)) 
#cpp_srcs变量下cpp文件替换成 .o文件
  1. foreach:循环函数

usage:$(foreach <var>,<list>,<text>)
把 list 的每个元素取出来 执行 text 的表达式,返回值是每次表达式返回结果的整个字符串,以空格分开。

  1. dir:取目录

usage:$(dir <names...>)
返回文件名序列的目录部分

  1. notdir:去掉所有路径

usage:$(notdir <names...>)

  1. filter:可以根据要求过滤文件

usage:$(filter <names...>)

# 找到所有lib开头的文件,返回文件名
libs    := $(notdir $(shell find /usr/lib -name lib*)) 
# 取出后缀为.a的静态库
a_libs  := $(filter %.a,$(libs))
# 取出后缀为.so的动态库
so_libs := $(filter %.so,$(libs))
  1. basename:去掉后缀

usage:$(basename <names...>)

条件语句

不能使用Tab 缩进,只能用空格

  1. 相等
ifeq ($(a),$(b))

else ifeq ()

else 

endif

  1. 不相等
ifneq ($(a),$(b))

else ifneq ()

else 

endif
  1. 定义
ifdef

endif 

常用选项

编译选项

  • -m64: 指定编译为 64 位应用程序
  • -std=: 指定编译标准,例如:-std=c++11、-std=c++14
  • -g: 包含调试信息
  • -w: 不显示警告
  • -O: 优化等级,通常使用:-O3
  • -I: 加在头文件路径前
  • fPIC: (Position-Independent Code), 产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的

链接选项

  • -l: 加在库名前面
  • -L: 加在库路径前面
  • -Wl,<选项>: 将逗号分隔的 <选项> 传递给链接器
  • -rpath=: “运行” 的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找

Ninja

概述

比较新的构建工具和 make 类似, 使用声明式的构建规则,它不需要像 **Makefile **那样冗长的文本文件,而是使用类似于 JSON 的格式。
它可以快速并行构建和最小化构建系统开销,某些情况下比 Make 快。适用于需要快速构建的大型项目。
在某些情况下,还可以将两者结合使用,使用 Make 作为高级构建系统的前端,而底层的构建任务则由 Ninja 执行,以兼顾灵活性和性能。

ninja

对比Makefile ,Ninja的规则文件名为xx.ninja

基本规则

目标语法

build <target>: <rule> <dependencies>
  • target:要构建的目标文件名。
  • rule:构建规则名称。
  • dependencies: 目标文件依赖列表
default <target> # 可以用default关键字指定默认构建目标

规则语法
Rules:定义如何构建目标,由名称,命令和可选属性组成

rule <name>
  command = <command>
  description = <description>

变量

variable = value # 等号赋值

调用变量使用@

一个综合例子

CFLAGS = -Wall -O2

rule cc
  command = gcc $CFLAGS -o $out $in
  description = CC $out

build main.o: cc main.c

Cmake

概述

跨平台的项目构建工具,可以根据配置生成不同构建系统的构建规则包括 **Makefile **和 ninja 等等。配置文件为CMakeLists.txt
默认使用的是 **make **工具,要使用 **Ninja **构建 ninja 规则可以使用-G选项

cmake -G Ninja /path/to/source

基本的编译流程

基本规则

基础使用

  1. 编写CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0)
project(test)
add_executable(app add.cpp main.cpp minus.cpp)
#也可以 add_executable(app add.cpp;main.cpp;minus.cpp)
  • cmake_minimum_required:Cmake 最低版本声明,可选但不加可能会有警告。
  • project:工程名,可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
# PROJECT 指令的语法是:
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
       [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
       [DESCRIPTION <project-description-string>]
       [HOMEPAGE_URL <url-string>]
       [LANGUAGES <language-name>...])
  • add_executable:定义生成一个可执行程序
add_executable(可执行程序名 源文件名称)
  1. 执行cmake命令

格式为cmake +CMakeLists.txt所在的路径。如果就在当前目录的话就可以执行cmake .,执行后会产生很多文件,其中就有Makefile

  1. 接着执行make即可

注释

# 行注释
#[[
块注释
]]

变量

定义

# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

使用:${}

set(src add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${src})

指定C++标准

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

指定输出路径

使用宏EXECUTABLE_OUTPUT_PATH

set(HOME /home/xxx/test)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
#也可以 set(EXECUTABLE_OUTPUT_PATH ./bin)

如果目录不存在会自动创建。
And,如果这里指定的是相对路径,那么./对应的就是生成的Makefile的目录。

文件包含

include_directories(${PROJECT_SOURCE_DIR}/include)
  • PROJECT_SOURCE_DIR:项目的根目录

文件搜索

方法一

aux_source_directory(< dir > < variable >)

查找dir下的所有文件并存储到variable变量中

# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src src)
  • CMAKE_CURRENT_SOURCE_DIR: 宏表示当前访问的CMakeLists.txt 文件所在的路径。

方法二
当目录很多的时候,就可以使用搜索文件的指令

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
  • GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中

举例

file(GLOB/GLOB_RECURSE src "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

综合例子

项目结构

~/test$ tree
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
└── src
    ├── add.cpp
    ├── main.cpp
    └── minus.cpp

CMakeLists.txt文件

# 设定cmake版本(可选)
cmake_minimum_required(VERSION 3.0)

# 设定项目名称
project(test)

# 设定编译器选项
set(CMAKE_CXX_STANDARD 11)

# 设定可行行文件输出路径
set(EXECUTABLE_OUTPUT_PATH ./bin)

# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)

# 设定源文件
file(GLOB src "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

# 添加可执行文件,生成一个可执行文件
add_executable(test ${src})

制作库文件

静态库

add_library(库名称 STATIC 源文件1 [源文件2] ...) 

前面提到静态库的命名lib自定义库名.a。这里的库名称只需要提供中间的自定义库名就行。其他会自动填充。

动态库

add_library(库名称 SHARED 源文件1 [源文件2] ...) 

和静态库一样,只需要指定名字。

指定输出路径

方法一
由于linux下有执行权限问题,动态库和可执行文件一样具有执行权限。所以可以使用一样的指定方法

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(test SHARED ${src})

方法二
静态库不具有执行权限,不能用同一个宏,得用LIBRARY_OUTPUT_PATH

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${src})
# 生成静态库
add_library(calc STATIC ${src})

链接库文件

链接静态库

link_libraries(<static lib> [<static lib>...])

可以全名也可以取中间的名称部分(去lib.a
非系统提供的库需要指定路径。

link_directories(<lib path>)

综合例子

# 设定cmake版本(可选)
cmake_minimum_required(VERSION 3.0)

# 设定项目名称
project(test)

# 设定编译器选项
set(CMAKE_CXX_STANDARD 11)


# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)

# 设定库文件输出路径
set(LIBRARY_OUTPUT_PATH ./lib)
# 设定可行行文件输出路径
set(EXECUTABLE_OUTPUT_PATH ./bin)
# 设定静态库路径
link_directories(${LIBRARY_OUTPUT_PATH})

# 设定源文件
file(GLOB lib "${CMAKE_CURRENT_SOURCE_DIR}/func/*.cpp")

file(GLOB exec "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")


# 添加可执行文件,生成一个可执行文件
add_library(calc STATIC ${lib})
link_libraries(calc)
add_executable(test ${exec})

链接动态库

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:要加载动态库的目标文件,可以是动态库,源文件或者可执行文件。
  • PRIVATE|PUBLIC|INTERFACE:文件的访问权限。
    • PUBLIC:public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
    • PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知调了啥库
    • INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。

默认PUBLIC。

  • item:动态库名,一般指定中间名字就行。

另外,动态库链接具有传递性,下面的代码中 D库也可以调用 B和C 库。

target_link_libraries(A B C)
target_link_libraries(D A)

AND,由于动态库链接阶段是不会被打包到可执行文件中,而是等文件执行的时候才开始调用,所以调用动态库的语句需要写到可执行文件生成之后。也就是

add_executable(test ${src})
target_link_libraries(test calc)

综合例子

# 设定cmake版本(可选)
cmake_minimum_required(VERSION 3.0)

# 设定项目名称
project(test)

# 设定编译器选项
set(CMAKE_CXX_STANDARD 11)


# 包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)

# 设定库文件输出路径
set(LIBRARY_OUTPUT_PATH ./lib)
# 设定可行行文件输出路径
set(EXECUTABLE_OUTPUT_PATH ./bin)
# 设定静态库路径
link_directories(${LIBRARY_OUTPUT_PATH})

# 设定源文件
file(GLOB lib "${CMAKE_CURRENT_SOURCE_DIR}/func/*.cpp")

file(GLOB exec "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

add_library(calc SHARED ${lib})

# 添加可执行文件,生成一个可执行文件
add_executable(test ${exec})
# 链接动态库
target_link_libraries(test calc)

日志

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
  • (无) :重要消息
  • STATUS :非重要消息
  • WARNING:CMake 警告, 会继续执行
  • AUTHOR_WARNING:CMake 警告 (dev), 会继续执行
  • SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤
  • FATAL_ERROR:CMake 错误, 终止所有处理过程

CMake的命令行工具会在 stdout 上显示 STATUS 消息,在 stderr上显示其他所有消息。CMake的GUI会在它的 log 区域显示所有消息。

常用变量操作

拼接

如果源文件不在同一个目录里,可能需要用到拼接

  • set 拼接
set(变量名1 ${变量名1} ${变量名2} ...)
  • list 拼接
list(APPEND <list> [<element> ...])

list功能很多,通过第一个功能参数来设置,如APPEND
是指字符串拼接。

删除

list(REMOVE_ITEM <list> <value> [<value> ...])

还是list,把功能参数换为REMOVE_ITEM即可

宏定义

自定义宏

add_definitions(-D宏名称)

预定义宏

PROJECT_SOURCE_DIR:使用cmake命令后紧跟的目录,一般是工程的根目录。
PROJECT_BINARY_DIR:执行cmake命令的目录。
CMAKE_CURRENT_SOURCE_DIR:当前处理的CMakeLists.txt所在的路径。
CMAKE_CURRENT_BINARY_DIR:target 编译目录。
EXECUTABLE_OUTPUT_PATH:目标二进制可执行文件的存放位置。
LIBRARY_OUTPUT_PATH:目标链接库文件的存放位置。
PROJECT_NAME:返回通过PROJECT指令定义的项目名称。
CMAKE_BINARY_DIR:项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径。

嵌套的CMake

在做大项目的时候,源码目录很多,都放在一个CMakeLists.txt会使得文件很复杂庞大,不易维护。那么可以对每个源文件目录都写上一个CMakeLists.txt,来便于管理。
嵌套的CMake和Linux目录一样都是树状结构,有根节点,父节点,子节点。其中的变量关系满足,

  • 根节点CMakeLists.txt中的变量全局有效
  • 父节点CMakeLists.txt中的变量可以在子节点中使用
  • 子节点CMakeLists.txt中的变量只能在当前节点中使用

CMakeLists.txt之间建立联系通过

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir:子节点的目录。
  • binary_dir:指定输出文件的路径,一般无需。
  • EXCLUDE_FROM_ALL:不需要被包含的文件

流程控制

逻辑计算符

基础逻辑

  • NOT
  • AND
  • OR

数值比较逻辑

  • LESS
  • GREATER
  • EQUAL
  • LESS_EQUAL
  • GREATER_EQUAL

字符串比较逻辑

  • STRLESS
  • STRGREATER
  • STREQUAL
  • STRLESS_EQUAL
  • STRGREATER_EQUAL

文件判断

  • EXISTS:文件是否存在,后面跟绝对路径
  • IS_DIRECTORY:是否是目录,后面跟绝对路径
  • IS_SYMLINK:是否软连接
  • IS_ABSOLUTE:是否绝对路径

其他判定逻辑

  • IN_LIST:判定某个元素是否存在于列表中
  • PATH_EQUAL:判定路径是否相同

条件

if(<condition>)
  <commands>
elseif(<condition>) 
  <commands>
else()              
  <commands>
endif() # 必须要有

条件判定的时候,

  • 如果是1, ON, YES, TRUE, Y, 非零值非空字符串时,条件判断返回 True
  • 如果是0, OFF, NO, FALSE, N, IGNORE, NOTFOUND空字符串时,条件判断返回 False

循环

**foreach**

  1. 法 1,遍历次数
foreach(<loop_var> RANGE <stop>)

foreach(<loop_var> RANGE <start> <stop> [<step>])
  • loop_var:存储每次循环取出的值
  • stop:表示从0开始,最大值为 stop,左右闭区间。
  • start,stop:也可以指定范围,左右闭区间。
  • step:可以指定跨度。
  1. 法 2,遍历列表
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
# 遍历多个列表
foreach(<loop_var>... IN ZIP_LISTS <lists>)
  • LIST:对应列表list,可以从setlist命令中获得

**while**

while(<condition>)
    <commands>
endwhile()

参考

5分钟理解make/makefile/cmake/nmake
GNC-Tutorial
CMake 保姆级教程

猜你喜欢

转载自blog.csdn.net/qq_43271194/article/details/132964274