Makefile

作者: leon0514 | 来源:发表于2022-12-12 10:27 被阅读0次

编译过程

  1. 流程图


    编译过程图
  2. 预处理 [Preprocessing]
  • 将所有的注释以空格代替
  • 将所有的#define删除,并展开所有的宏定义
  • 处理条件编译指令 #if #ifdef #elif #else #endif
  • 处理#include, 展开文件包含
  • 保留编译器需要使用的#progma指令
    预处理指令示例:gcc -E *.c -o *.i
  1. 编译优化
  • 对预处理生成的文件进行语法分析、词法分析、语义分析
    语法分析:分析表达式是否遵循语法规则
    词法分析:分析关键字,标识符,立即数是否合法
    语义分析:在语法分析基础上进一步分析表达式是否合法
  • 分析结束后进行 [代码优化] 生成相应的汇编代码文件
    编译指令示例:gcc -S *.i -o *.s
  1. 汇编
  • 汇编过程是用汇编器将汇编代码转变为机器可以执行的指令,也就是机器指令,也称为目标文件(.o)。
  • 每条汇编指令几乎都对应一条机器指令
    汇编指令示例: gcc - c *.s -o *.o
  1. 链接
  • 链接是指将目标文件最终生成可执行文件。
    静态链接(.a、.lib):目标文件直接加入到可执行文件
    动态链接(.so、.dll):在程序启动后才动态加载目标文件

6.常用 g++ (gcc) 编译选项

  • -shared
    指定生成动态链接库
  • -static
    指定生成静态链接库
  • -fPIC
    表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 理念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方
  • -L.
    表示要连接的库所在的目录。
  • -l
    指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。
  • -Wall
    生成所有警告信息。
  • -ggdb
    此选项将尽可能的生成gdb 的可以使用的调试信息。
  • -g
    编译器在编译的时候产生调试信息。
  • -c
    只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件) 。
  • -Wl,options
    把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。
  • -Wl,-rpath=选项
    链接器在可执行文件头中记录动态库的路径,动态加载器运行时读取动态库路径,加载动态库。
  • -O0
    不进行优化处理。
  • -O 或 -O1
    优化生成代码
  • -O2
    进一步优化。
  • -O3
    -O2 更进一步优化,包括 inline 函数。
  1. 分析程序依赖项
  • readelf -d xxx
    查看程序依赖的so文件,以及rpaths 路径
  • ldd
    指令可以查看该程序依赖的so,查找到的具体路径。看看是否符合预期
  1. 链接时so、a文件查找顺序
    1. -L配置的目录
    1. g++内置的系统目录,例如/usr/lib…
    1. 系统环境变量(例如LIBRARY_PATH )指定的目录
  1. 运行时so文件查找顺序
    1. 应用程序的当前目录
    1. 可执行文件中储存的rpath(run path)。readelf -d xxx指令可以查看文件的runpath信息。如果该选项指定了依旧失效,说明依赖的so文件还存在更多依赖在其他目录没有明确(常用)
    1. 环境变量指定的目录(例如LD_LIBRARY_PATH)

Makefile基本语法

  1. 数据类型
    字符串、字符串数组
  2. 定义变量
    var := folder, 定义变量var, string类型,值为folder
  3. 定义数组
    var := hello world folder,定义变量var,为数组类型,值是["hello", "world", "folder"]
  4. 定义的多种方式
    = 赋值 var = folder 递归赋值,Makefile全部执行后决定取值(不常用)
    := 赋值 var := folder 基本赋值,当前所在位置决定取值(常用)
    ?= 赋值 var ?= folder 如果没有赋值,则赋值为folder
    += 赋值 var += folder 添加值,在var后面添加值。可以认为数组后边增加元素
    append,var := hello, var += world,结果是hello world,中间有空格
  5. $(var)
    ${var},在这个位置解释为var的值,例如: var2 := $(var)
  6. $(func param),调用Make提供的内置函数
    例如:var := $(shell echo hello),定义var的值为执行shell echo hello后的输出
    $(info $(var)),直接打印var变量内容
    var := $(subst a,x,aabbcc),替换aabbcc中的a为x后赋值给var,结果xxbbcc
  7. 逻辑语法: ifeqifneqifdefifndef
ifeq($(var), depends)        
name := hello      
endif
  1. Makefile系统通配符
  • * 匹配0个或者任意个字符
  • ? 匹配任意一个字符
  • [] 我们可以指定匹配的字符放在[]
  • % 字符作用类似于通配符*,它和 * 的区别是,模式匹配字符可以对目标文件与依赖文件进行匹配。比如说我们在写 makefile 的时候,经常会写这样的一条规则%.o:%.c
    这里的 % 代表的是一个文件名,也就是一个字符串。首先,所有的 .o 文件会组成一个列表,然后挨个被拿出来,% 表示当前拿出来的 %.o 文件的文件名,然后根据文件名 % 来寻找和 .o 文件同名的 %.c 文件,并把取出的 %.o 文件和寻找到的 %.c 文件用于执行后面的命令。这是 makefile 中自动匹配的一种规则。

在变量定义的时候通配符*不会自动展开,需要使用wildcard函数来实现

  1. Makefile自动变量
  • $@ 表示目标文件
  • $^ 表示所有的依赖文件
  • $< 表示第一个依赖文件
  • $? 表示比目标还要新的依赖文件列表
  1. Makefile函数
  • 通配符扩展函数
    $(wildcard <pattern...>)
    功能:它被展开为已经存在的、使用空格分开的、匹配此模式的指定文件夹下的所有文件列表
    返回:符合条件的文件列表
  • 字符串替换函数
    $(subst <from>,<to>,<text>)
    功能: 把字符串<text> 中的 <from> 替换为 <to>
    返回: 替换过的字符串

# Makefile 内容
all:
    @echo $(subst t,e,maktfilt)  <-- 将t替换为e

# bash 中执行 make
$ make
makefile
  • 模式字符串替换函数:
    $(patsubst <pattern>,<replacement>,<text>)
    功能: 查找<text>中的单词(单词以"空格", "tab", "换行"来分割) 是否符合<pattern>, 符合的话,用<replacement>替代
    返回: 替换过的字符串
# Makefile 内容
all:
    @echo $(patsubst %.c,%.o,programA.c programB.c)

# bash 中执行 make
$ make
programA.o programB.o
  • 模式匹配替换
    说明:使用匹配符%匹配变量,使用 % 保留变量值中的指定字符串,然后其他部分使用指定字符串代替。
.PHONY: all
SRC := main.c sub.c
OBJ := $(SRC:%.c=%.o)
all:
    @echo "SRC = $(SRC)"
    @echo "OBJ = $(OBJ)"
# 执行结果
# make
SRC = main.c sub.c
OBJ = main.o sub.o
  • 去空格函数
    $(strip <string>)
    功能: 去掉 <string> 字符串中开头和结尾的空字符
    返回: 被去掉空格的字符串值
# Makefile 内容
VAL := "       aa  bb  cc "
all:
    @echo "去除空格前: " $(VAL)
    @echo "去除空格后: " $(strip $(VAL))
# bash 中执行 make
$ make
去除空格前:         aa  bb  cc 
去除空格后:   aa bb cc
  • 查找字符串函数
    $(findstring <find>,<in>)
    功能: 在字符串 <in> 中查找 <find> 字符串
    返回: 如果找到, 返回 <find> 字符串, 否则返回空字符串
# Makefile 内容
VAL := "       aa  bb  cc "
all:
    @echo $(findstring aa,$(VAL))
    @echo $(findstring ab,$(VAL))
# bash 中执行 make
$ make
aa
  • 过滤函数
    $(filter <pattern...>,<text>)
    功能: 以 <pattern> 模式过滤字符串 <text>, 保留 符合模式 <pattern>的单词, 可以有多个模式
    返回: 符合模式 <pattern> 的字符串
# Makefile 内容
all:
    @echo $(filter %.o %.a,program.c program.o program.a)
# bash 中执行 make
$ make
program.o program.a
  • foreach 语法
    $(foreach <var>,<list>,<text>)
# Makefile 内容
targets := a b c d
objects := $(foreach i,$(targets),$(i).o)
all:
    @echo $(targets)
    @echo $(objects)
# bash 中执行 make
$ make
a b c d
a.o b.o c.o d.o
  • shell 语法
    $(shell <shell command>)
    它的作用就是执行一个shell命令, 并将shell命令的结果作为函数的返回.
    作用和 `<shell command>` 一样, ` 是反引号

通用Makefile

# 检索src目录查找cpp为后缀的文件,用shell指令的find
srcs := $(shell find src -name "*.cpp")

# 将srcs的后缀为.cpp的替换为.o
objs := $(srcs:.cpp=.o)

# 将src/前缀替换为objs前缀,让.o文件方到objs目录下
objs := $(objs:src/%=objs/%)
# 所有objs文件的.o文件改为.mk, 得到mks
mks := $(objs:.o=.mk)


# 1. 增加include_paths选项, 因为头文件需要他们
include_paths := /path1/include \
             /path2/include

# 2. 增加ld_liberarys选项,因为链接需要他们

library_paths := /path1/lib \
             /path2/lib

ld_librarys := m curl ssl

# 3. 将每一个头肩路径前面增加-I, 库文件路径前面增加-L, 链接选项前面加上-l
# -I 配置头文件路径
# -L 配置库路径
# -lname 配置依赖的so
# 增加run path 变量,语法为
# g++ main.o test.o -o out.bin -Wl,-rpath=/path1/lib  /path2/lib
run_paths := $(library_paths:%=-Wl,-rpath=%)
include_paths := $(include_paths:%=-I%)
library_paths := $(library_paths:%=-L%)
ld_librarys   := $(ld_librarys:%=-l%)

# 4. 增加compile_flags,增加编译选项,例如我们需要C++11特性等,
# -w避免警告,-g生成调试信息
# -O0优化级别关闭
compile_flags := -std=c++11 -w -g -O0 $(include_paths)
link_flags := $(library_paths) $(ld_librarys)

# 所有的头文件依赖产生的makefile文件,进行include
ifneq ($(MAKECMDGOALS), clean)
include $(mks)
endif

# 定义objs下的o文件,依赖src下对应的cpp文件
# $@ = 左边的生成项
# $< = 右边的依赖项第一个
# 5. 将编译选项增加到g++编译后面
objs/%.o : src/%.cpp
    @echo 编译$<,生成$@, 目录是:$(dir $@)
    @mkdir -p $(dir $@)
    g++ -c $< -o $@ $(compile_flags)

# $^ = 右边的依赖项全部
# 6. 将链接选项增加到g++链接后面
workspace/pro : $(objs)
    @echo 这里所有的依赖项是6[$^]
    @echo 链接$@
    g++ $^ -o $@ $(link_flags)
# 这里由#include $(mks)触发执行
# 不存在.mk文件,这里会执行产生.mk文件
objs/%.mk : src/%.cpp
    @echo 生成依赖$@
    @mkdir -p $(dir $@)
    @g++ -MM $< -MF $@ -MT $(@:.mk=.o) $(compile_flags)
# 定义简洁指令, make pro即可生成程序
pro : workspace/pro
    @echo 编译完成

# 定义make run, 编译好pro后执行
run : pro
    @cd workspace && ./pro

# 定义make clean, 清理编译留下的垃圾
clean :
    @rm -rf workspace/pro objs

debug:
    @echo objs is [$(objs)]

.PHONY : pro run debug clean

相关文章

  • 编写Makefile及简单分析

    makefile的好处:一次编写,终身受益 makefile的命名规则: makefile Makefile ma...

  • Makefile 工程管理

    Ⅰ Makefile的用途 Ⅱ Makefile的构成 Ⅲ Makefile构成-----规则 Ⅳ Makefil...

  • win_c/c++ mess01

    1. win makefile 1.1 win makefile,eg: 1.2 makefile explai...

  • [C] Makefile

    Makefile Blog [Makefile的简便写法] [Makefile]菜鸟教程 [gcc编译声明问题] ...

  • 迅为IMX6ULL开发板Ubuntu下C编程入门(二)

    本文是介绍3.3 初识 Makefile+3.4Makefile语法 3.3初识Makefile 3.3.1什么是...

  • Make

    make 目标 顶层 Makefile 作为入口, 来调用其他 makefile, 顶层 makefile 一般有...

  • Makefile学习笔记

    Makefile学习笔记 学习Makefile的资料 《跟我一起写makefile》 《GUN make manu...

  • Linux Kernel Makefiles特殊符号

    Makefile基本规则: Makefile文件中可使用特殊的符号简化Makefile文件的书写。 1、$@:表示...

  • makefile入门一

    Linux下Makefile详解 下面就来看如何写Makefile文件:Makefile文件编写规范: 下来看事例...

  • Makefile

    Makefile for boot Makefile Makefile对格式有要求:每一行文本除非顶头开始,如果需...

网友评论

      本文标题:Makefile

      本文链接:https://www.haomeiwen.com/subject/sswqqdtx.html