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文件 -> 链接成可执行文件
- 用编辑器编写源代码(文本),如
.c
文件 - 然后通过预处理器 cpp 变为修改后的源代码(文本)(导入头文件内容,删除注释等)如
.i
文件。 - 接着编译器 ccl 将其转换为汇编程序(文本),如
.s
文件 - 汇编器 as 编译代码生成可重定位目标文件(二进制),如
.o
。 - 最后链接器 **ld **链接目标代码生成可执行文件(二进制)。
举个例子
以hello.c
为例
C 相关后缀文件
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.c
, add.c
,minus.c
,其中后两个为功能函数。
gcc -c main.c add.c minus.c
那么编译后得到三个对象文件main.o
, add.o
,minus.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++ 相关后缀文件
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.c
, add.c
,minus.c
,其中后两个为功能函数。
g++ -c main.c add.c minus.c
那么编译后得到三个对象文件main.o
, add.o
,minus.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 的。
流程如下:
make
概述
老牌的自动化构建和编译项目工具,使用名为 Makefile 的文件来定义构建规则。根据文件的依赖关系和时间戳来确定哪些文件需要重新构建,以确保构建是有效的。灵活且功能丰富,适用于各种项目,特别是在Unix/Linux环境下。
大多数IDE都集成了make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake等。
常用的命令
-
make
:运行 make 命令时,它将查找当前目录中的 Makefile 文件并执行默认的构建任务。通常,make 命令不需要参数。 -
make <target>
:运行 make 命令时,你可以指定要构建的目标。例如,make all
或make myprogram
将构建名为 “all” 或 “myprogram” 的目标。这些目标需要在 Makefile 中定义。 -
make -f <filename>
:使用-f
选项可以指定一个不同的 Makefile 文件的名称,而不使用默认的 Makefile。 -
make clean
:通常,项目的 Makefile 中会包含一个 clean 目标,用于清除生成的中间文件和可执行文件。运行make clean
将执行清理操作。 -
make install
:如果 Makefile 中包含一个 install 目标,运行 make install 可以安装项目的可执行文件或其他文件到系统中的指定位置。 -
make uninstall
:类似于 install,如果 Makefile 包含一个 uninstall 目标,运行make uninstall
可以卸载先前安装的文件。 -
make <variable>=<value>
:你可以通过命令行覆盖 Makefile 中的变量的值。例如,make CFLAGS="-O2"
将覆盖 CFLAGS 变量的值为 “-O2”。 -
make -n
:使用-n
选项可以进行模拟构建,而不实际执行命令。这可以帮助你查看 make 将要执行的命令,但不会真正运行它们
Makefile
基础规则
基本结构
每个Makefile包含一个或者多个目标
targets : prerequisties
[tab键]command # 必须是tab键
#或者
targets : prerequisites ; command
-
target
:目标文件,可以是 OjectFile,也可以是执行文件,还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。 -
prerequisite
:要生成那个 target 所需要的依赖文件。 -
command
:是 make 需要执行的命令
运行规则
- make 会在当前目录下找到一个名字叫
Makefile
或makefile
的文件 - 如果找到,它会找文件中第一个目标文件(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
常用运算符
- **赋值 **
=
:类似一般编程语言的赋值符号
- 立即赋值
:=
:定义变量的时候立即求值,然后值不可更改
- 默认赋值
?=
:如果变量已经定义就不进行任何操作,否则就进行赋值
- 累加
+=
:就是一般编程语言里面的,右边的加到左边。
- 续行
\
:一行写不完,可以用这个继续写
- 通配符
*
:匹配任意字符串,用于目录名或者文件名%
:匹配任意字符串,并将字符串作为变量使用
常用函数
函数调用和变量使用类似,用$
标识,语法如下,$(fn, arguments) or ${fn, arguments}
fn
:函数名arguments
:函数参数
- shell:调用 shell 命令
usage:$(shell <command> <arguments>)
返回 shell 命令的执行结果。
- subst: 字符串替换函数
usage:$(subst <old>,<new>,<text>)
返回替换后的新字符串,text 中的 old 替换为 new
- patsubst:模式字符串替换函数
usage:$(patsubst <pattern>,<replacement>,<text>)
从 text 中取出 pattern 替换为 replacement。可用任意长度字符串通配符 %
示例:
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
#cpp_srcs变量下cpp文件替换成 .o文件
- foreach:循环函数
usage:$(foreach <var>,<list>,<text>)
把 list 的每个元素取出来 执行 text 的表达式,返回值是每次表达式返回结果的整个字符串,以空格分开。
- dir:取目录
usage:$(dir <names...>)
返回文件名序列的目录部分
- notdir:去掉所有路径
usage:$(notdir <names...>)
- 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))
- basename:去掉后缀
usage:$(basename <names...>)
条件语句
不能使用Tab 缩进,只能用空格
- 相等
ifeq ($(a),$(b))
else ifeq ()
else
endif
- 不相等
ifneq ($(a),$(b))
else ifneq ()
else
endif
- 定义
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
基本的编译流程
基本规则
基础使用
- 编写
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(可执行程序名 源文件名称)
- 执行
cmake
命令
格式为cmake
+CMakeLists.txt
所在的路径。如果就在当前目录的话就可以执行cmake .
,执行后会产生很多文件,其中就有Makefile
- 接着执行
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,遍历次数
foreach(<loop_var> RANGE <stop>)
foreach(<loop_var> RANGE <start> <stop> [<step>])
-
loop_var
:存储每次循环取出的值 -
stop
:表示从0开始,最大值为 stop,左右闭区间。 -
start,stop
:也可以指定范围,左右闭区间。 -
step
:可以指定跨度。
- 法 2,遍历列表
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
# 遍历多个列表
foreach(<loop_var>... IN ZIP_LISTS <lists>)
-
LIST
:对应列表list
,可以从set
,list
命令中获得
**while**
while(<condition>)
<commands>
endwhile()
文章来源:https://uudwc.com/A/0kava
参考
5分钟理解make/makefile/cmake/nmake
GNC-Tutorial
CMake 保姆级教程文章来源地址https://uudwc.com/A/0kava