• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    生成汇编语言

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

    生成目标对象文件

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

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

    gcc main.c
    gcc main.c -o xxx
    
    • 1
    • 2

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

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

    gcc main.c add.c minus.c -o exec
    ./exec
    
    • 1
    • 2

    static library && shared library

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

    静态库

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

    gcc -c [.c] -o [功能函数] 
    gcc -c [.c] [.c] ...
    
    • 1
    • 2

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

    gcc -c main.c add.c minus.c
    
    • 1

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

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

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

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

    得到静态库liboperation.a

    链接为可执行文件

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

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

    gcc main.c liboperation.a -o exec
    
    • 1

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

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

    动态库

    二进制对象文件

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

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

    也可以直接编成库文件

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

    链接为可执行文件

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

    C++ 程序编译

    C++ 相关后缀文件

    fundamentalcpp.png

    Executable program

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

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

    生成汇编

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

    生成二进制对象文件

    g++ -c helloworld.c
    g++ -c helloworld.c -o harumph.o
    # 编译多个 .c 文件
    g++ -c helloworld.c helloworld1.c helloworld2.c
    
    • 1
    • 2
    • 3
    • 4

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

    g++ helloworld.c
    g++ helloworld.c -o howdy
    
    • 1
    • 2

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

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

    static library && shared library

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

    静态库

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

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

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

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

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

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

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

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

    得到静态库liboperation.a

    链接为可执行文件

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

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

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

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

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

    动态库

    二进制对象文件

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

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

    也可以直接编成库文件

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

    链接为可执行文件

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

    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 :运行 make 命令时,你可以指定要构建的目标。例如,make all make myprogram 将构建名为 “all” 或 “myprogram” 的目标。这些目标需要在 Makefile 中定义。
    3. make -f :使用 -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 =:你可以通过命令行覆盖 Makefile 中的变量的值。例如,make CFLAGS="-O2" 将覆盖 CFLAGS 变量的值为 “-O2”。
    8. make -n:使用-n 选项可以进行模拟构建,而不实际执行命令。这可以帮助你查看 make 将要执行的命令,但不会真正运行它们

    Makefile

    基础规则

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

    targets : prerequisties
    [tab键]command # 必须是tab键
    #或者
    targets : prerequisites ; command
    
    • 1
    • 2
    • 3
    • 4
    • 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    变量

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

    cpp := src/main.cpp 
    obj := objs/main.o
    
    • 1
    • 2

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

    cpp := src/main.cpp 
    obj := objs/main.o
    
    $(obj) : ${cpp}
    	@g++ -c $(cpp) -o $(obj)
    
    compile : $(obj)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    预定义变量

    • $@: 目标(target)的完整名称
    • $<: 第一个依赖文件(prerequisties)的名称
    • $^: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件
    cpp := src/main.cpp 
    obj := objs/main.o
    
    $(obj) : ${cpp}
    	@g++ -c $< -o $@
    	@echo $^
    
    compile : $(obj)
    .PHONY : compile
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    常用运算符
    1. **赋值 **

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

    1. 立即赋值

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

    1. 默认赋值

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

    1. 累加

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

    1. 续行

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

    1. 通配符

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

    常用函数

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

    1. shell:调用 shell 命令

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

    1. subst: 字符串替换函数

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

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

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

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

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

    1. dir:取目录

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

    1. notdir:去掉所有路径

    usage:$(notdir )

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

    usage:$(filter )

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

    usage:$(basename )

    条件语句

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

    1. 相等
    ifeq ($(a),$(b))
    
    else ifeq ()
    
    else 
    
    endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 不相等
    ifneq ($(a),$(b))
    
    else ifneq ()
    
    else 
    
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 定义
    ifdef
    
    endif 
    
    • 1
    • 2
    • 3

    常用选项

    编译选项

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

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

    rule 
      command = 
      description = 
    
    • 1
    • 2
    • 3

    变量
    variable = value # 等号赋值
    
    • 1

    调用变量使用@

    一个综合例子

    CFLAGS = -Wall -O2
    
    rule cc
      command = gcc $CFLAGS -o $out $in
      description = CC $out
    
    build main.o: cc main.c
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Cmake

    概述

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

    cmake -G Ninja /path/to/source
    
    • 1

    基本的编译流程

    基本规则

    基础使用
    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)
    
    • 1
    • 2
    • 3
    • 4
    • cmake_minimum_required:Cmake 最低版本声明,可选但不加可能会有警告。
    • project:工程名,可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
    # PROJECT 指令的语法是:
    project( [...])
    project(
           [VERSION [.[.[.]]]]
           [DESCRIPTION ]
           [HOMEPAGE_URL ]
           [LANGUAGES ...])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • add_executable:定义生成一个可执行程序
    add_executable(可执行程序名 源文件名称)
    
    • 1
    1. 执行cmake命令

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

    1. 接着执行make即可
    注释
    # 行注释
    #[[
    块注释
    ]]
    
    • 1
    • 2
    • 3
    • 4

    变量

    定义

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

    使用:${}

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

    指定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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    指定输出路径

    使用宏EXECUTABLE_OUTPUT_PATH

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

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

    文件包含
    include_directories(${PROJECT_SOURCE_DIR}/include)
    
    • 1
    • PROJECT_SOURCE_DIR:项目的根目录
    文件搜索

    方法一

    aux_source_directory(< dir > < variable >)
    
    • 1

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

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

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

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

    举例

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

    综合例子

    项目结构

    ~/test$ tree
    .
    ├── build
    ├── CMakeLists.txt
    ├── include
    │   └── head.h
    └── src
        ├── add.cpp
        ├── main.cpp
        └── minus.cpp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    制作库文件

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

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

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

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

    指定输出路径

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

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

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

    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
    # 生成动态库
    #add_library(calc SHARED ${src})
    # 生成静态库
    add_library(calc STATIC ${src})
    
    • 1
    • 2
    • 3
    • 4
    • 5

    链接库文件

    链接静态库
    link_libraries( [...])
    
    • 1

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

    link_directories()
    
    • 1

    综合例子

    # 设定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})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    链接动态库
    target_link_libraries(
         
         ... 
        [ ...]...)
    
    • 1
    • 2
    • 3
    • 4
    • 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)
    
    • 1
    • 2

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

    add_executable(test ${src})
    target_link_libraries(test calc)
    
    • 1
    • 2

    综合例子

    # 设定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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    日志

    message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
    
    • 1
    • (无) :重要消息
    • 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} ...)
    
    • 1
    • list 拼接
    list(APPEND  [ ...])
    
    • 1

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

    删除
    list(REMOVE_ITEM   [ ...])
    
    • 1

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

    宏定义

    自定义宏
    add_definitions(-D宏名称)
    
    • 1

    预定义宏

    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])
    
    • 1
    • 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()
      
    elseif() 
      
    else()              
      
    endif() # 必须要有
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    条件判定的时候,

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

    **foreach**

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

    **while**

    while()
        
    endwhile()
    
    • 1
    • 2
    • 3

    参考

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

  • 相关阅读:
    【工作记录】MQTT介绍、安装部署及springboot集成@20230912
    Dubbo3应用开发—Dubbo直连开发相关概念:通信、协议、序列化
    week3 map
    力扣1726. 同积元组
    生产事故-记一次特殊的OOM排查
    Spring 源码阅——忽略感知接口对应成员变量的自动装配
    C语言求 3*3 矩阵对角线之和
    前端内存泄漏和溢出的情况以及解决办法
    【精选】项目管理工具——Maven详解
    提取作者用户名,帖子内容,回复时间
  • 原文地址:https://blog.csdn.net/qq_43271194/article/details/132964274