Makefile基础
gcc命令
通过gcc命令实现编译
main.c
#include<stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
1.预处理
gcc -E main.c -o main.i
-E
:预处理;-o
: 将结果输出到指定文件中;
2. 编译
生成汇编语言
gcc -S mian.c -o main.s
-S
:进行预处理和汇编操作;
3. 汇编
生成机器语言
gcc -c mian.c -o mian.o
-c
:生成目标文件
4.链接
gcc mian.c -o main
静态文件库
三个.c文件:main.c
, add.c
, minus.c
目标:将add.c
, minus.c
编译为静态文件库
mian.c
#include<stdio.h>
int add(int a, int b);
int minus(int a, int b);
int main()
{
printf("Hello world\n");
printf("add = %d\n", add(7, 5));
printf("minus= %d\n", minus(7, 5));
return 0;
}
add.c
int add(int a, int b)
{
return a + b;
}
minus.c
int minus(int a, int b)
{
return a - b;
}
# 1.编译成.o文件
gcc -c [.c] [.c] ...
# example
gcc -c add.c minus.c
# 2. 编译静态库
ar -r [lib自定义库名.a] [.o] [.o] ...
# example
ar -r liboperation.a add.o minus.o
# 3. 链接为可执行文件
gcc [.c] [.a] -o [自定义输出文件名]
or
gcc [.c] -o [自定义输出文件名] -l[库名] -L[库路径]
# example
gcc main.c liboperation.a -o main
总结:
.a
为后缀;lib
作为前缀;动态文件库
# 1. 编译为.o文件
gcc -c -fpic [.c] [.c]...
# example
gcc -c -fpic add.c minus.c
# 编译为静态库
gcc -shared [.o] [.o]... -o [lib自定义库名.so]
# example
gcc -shared add.o minus.o -o liboperation.so
# 链接
gcc [.c/.cpp] -o [自定义可执行文件名] [动态库路径]
or
gcc [.c] -o [自定义输出文件名] -l[库名] -L[库路径] -WL,rpath=[库路径]
# example
gcc main.c -o main ./liboperation.so
-fpic
:生成与位置无关的代码(Position Independent Code)- 动态文件库后缀为
.so
Makefile
基本格式
targets : prerequisties
[tab键]command
- targets:目标文件
- prerequisties:依赖文件
- command:执行命令
变量
$@
: 目标(target)的完整名称$<
: 第一个依赖文件(prerequisties)的名称$^
: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件
#定义变量
obj := main.o
cfile := main.c
${obj}: ${cfile}
gcc -c $^ -o $@
debug:
@echo ${obj}
@echo ${cfile}
clear:
rm *.o
.PHONY: clear
.PHONY
伪目标@
可以隐藏命令,终端不输出$^
表示main.c
$@
表示main.o
常用符号
=
- 简单的赋值运算符
- 用于将右边的值分配给左边的变量
- 如果在后面的语句中重新定义了该变量,则将使用新的值
:=
- 立即赋值运算符
- 用于在定义变量时立即求值
- 该值在定义后不再更改
- 即使在后面的语句中重新定义了该变量
?=
- 默认赋值运算符
- 如果该变量已经定义,则不进行任何操作
- 如果该变量尚未定义,则求值并分配
+=
- 累加
*和%
*
: 通配符表示匹配任意字符串,可以用在目录名或文件名中%
: 通配符表示匹配任意字符串,并将匹配到的字符串作为变量使用
常用函数
$(fn arguments) or ${fn arguments}
- fn: 函数名
- arguments: 函数参数,参数间以逗号
,
分隔,而函数名和参数之间以“空格”分隔
1. shell: 调用shell命令
$(shell <command> <arguments>)
- 功能:调用 shell 命令 command
- 返回:函数返回 shell 命令 command 的执行结果
#获取src下的所有.c文件
c_src := $(shell find src -name "*.c")
debug:
@echo $(c_src)
.PHONY:debug
2 subst:字符串替换
$(subst <from>,<to>,<text>)
- 功能:把字串中的字符串替换成
- 返回:函数返回被替换过后的字符串
#获取src下的所有.c文件
c_src := $(shell find src -name "*.c")
# 将src文件夹更换为obj文件夹
c_obj := $(subst src, obj, $(c_src))
debug:
@echo $(c_src)
@echo $(c_obj)
.PHONY:debug
3. patsubst: 字符串替换
- 功能:通配符
%
,表示任意长度的字串,从 text 中取出 patttern, 替换成 replacement - 返回:函数返回被替换过后的字符串
#获取src下的所有.c文件
c_src := $(shell find src -name "*.c")
# 将src文件夹更换为obj文件夹并将后缀替换为.o
c_obj := $(patsubst src%.c, obj%.o, $(c_src))
debug:
@echo $(c_src)
@echo $(c_obj)
.PHONY:debug
4. foreach:循环函数
$(foreach <var>,<list>,<text>)
- 功能:把字串中的元素逐一取出来,执行包含的表达式
- 返回:所返回的每个字符串所组成的整个字符串(以空格分隔)
# .头文件的地址
library_paths := ./inc \
/usr/local/include
# 将头文件前面加上-L
library_paths := $(foreach iterm, $(library_paths), -L$(library_paths))
debug:
@echo $(library_paths)
.PHONY:debug
5 dir:返回子目录
$(dir <names...>)
- 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前 的部分。如果没有反斜杠,那么返回“./”。
- 返回:返回文件名序列的目录部分。
#获取src下的所有.c文件
c_src := $(shell find src -name "*.c")
# 获取src文件夹路径
src_path := $(dir $(c_src))
debug:
@echo $(src_path)
.PHONY:debug
6 notdir : 去掉文件绝对路径,保留文件名
$(notdir <names...>)
7. filter:过滤
$(filter <names...>)
8 basename:去掉后缀
$(basename <names...>)
example
# 获取静态库和动态库名称
#获取/usr/lib 下以lib为前缀的路径
lib_path := $(shell find /usr/lib -name "lib*")
# 去掉文件路径,获取文件名
lib_name := $(notdir $(lib_path))
# 过滤出静态库和动态库
a_libs := $(filter %.a, $(lib_name))
so_libs := $(filter %.so, $(lib_name))
# 获取库文件名称
a_libs := $(basename $(a_libs))
so_libs := $(basename $(a_libs))
# 去掉前缀lib
a_libs := $(subst lib, , $(a_libs))
so_libs := $(subst lib, , $(so_libs))
debug:
@echo $(so_libs)
.PHONY:debug
编译选项
-std=
: 指定编译标准,例如:-std=c++11、-std=c++14-g
: 包含调试信息-w
: 不显示警告-O
: 优化等级,通常使用:-O3-I
: 加在头文件路径前fPIC
: (Position-Independent Code), 产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的
链接选项
-l
: 加在库名前面-L
: 加在库路径前面-Wl,<选项>
: 将逗号分隔的 <选项> 传递给链接器-rpath=
: "运行" 的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找