Makefile

Makefile系列文章目录


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


以新手视野看待Makefile

提示: 作为一个从业者者,在课堂外第一次自己动手写makefile,参考了不少网上资料结合之前在工作中遇到的Makefile,这里写一篇文章进行学习记录。

一、Makefile是什么?

在我们学校stm32使用keil或者codeblock中,我们一般只需要在project中add 文件就行,其会自动编译成可执行文件,但是在一般开发过程中并不是,我们一般需要撇开ide的编译器去编译,并且在不同厂商提供的交叉编译器去使用编译代码,编译器就显得冗余(编写代码除外(vi大神再除外))
在嵌入式linux开发中,一般用arm-linux-gexxxxxx-gcc等编译器去编辑,对于c初学者可能只有一个main.c的话可能只需要gcc -o main main.c 即可,但是对于一些小型demo就不方便了,得一个个编译成.o以后再进行编译,这时候Makefie就起到作用了,脚本型makefile能够一本万利,书写一次即可,对于经常改动代码调参也方便

二、学习步骤

1 学习之前先看看其gcc参数

-E 预处理 去宏 注释空白行等 输出x.i文件
-S 编译 检查语法规范 输出x.s文件
-c 只编译不链接得到二进制文件 产生.o文件,就是obj文件,不产生执行文件
-o指定输出文件名 不指定的话默认 a.out 并非最终执行文件

-D 向程序中“动态”注册宏定义 gcc -o ddd main.c add.c minus.c -D DDDDDDD=444

-Wall 显示所有警告信息
-w 关闭编译时的警告,数据转换以及可能未使用变量

-O3 意思是开启编译优化,等级为三。

-I 指定头文件所在目录位置
-g 编译时添加调试文件,用于 gdb 调试
-l 指定动态库库名
-L 指定动态库路径

-shared 如果想创建一个动态链接库,可以使用 gcc的-shared选项。输入文件可以是源文件、汇编文件或者目标文件。
-fPIC 选项作用于编译阶段,告诉编译器产生与位置无关代码 使用相对地址。

在这里插入图片描述

1.认识简单的gcc

demo代码很简单,但是对于库文件代码记得添加容错处理

int add(int param1,int param2)
{
	return param1+param2;
}
#include<stdio.h>
int add(int param1,int param2);
int minus(int param1,int param2)
{
	return param1-param2;
}
#include<stdio.h>
int minus(int param1,int param2);
#include<stdio.h>
int main(int argv ,char **argc)
{
	int a=0;
	int b=1;
	int c = add(a,b);
	printf("add %d \n",c);
	int d = minus(a,b);
	printf("minus %d \n",d);

}

1.最简单的

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

2.使用静态库

gcc -c add.c
gcc -c minus.c
ar -r libaddminus.a add.o minus.o
gcc -o main main.c libaddminus.a -D  DDDDDDD=1

3.使用动态库

gcc -shared -fpic -o libmyhello.so add.o minus.o
gcc main.c -o main  -lmyhello -L./
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$pwd

2.书写简单的Makefile

首先我们先了解Makefile的书写规则

目标文件 : 依赖文件1 依赖文件2 依赖文件3 
	执行指令1
	执行指令2
	etc.....
目标文件 : 通常为.o或者可执行文件
依赖文件:即目标文件由哪些文件生成
执行指令:这个后面有讲(注意:执行指令前面有个tab)

目标文件被设置为第一个目标,并且依赖文件1 依赖文件2 依赖文件3 被列为目标文件的依赖。当你在命令行中运行make时,make命令会自动寻找准备好的依赖文件1 依赖文件2 依赖文件3 并执行目标文件编译命令
接下来试试最简单的,我们在终端输入

mkdir makestudy  //创建·一个学历makefile目录
cd makestudy/    // 进入此目录

以上命令为我们创建一个文件夹
然后又我们创建"Makefile"并写入以下命令

main:main.c
	gcc -o main main.c

然后又我们创建"main.c"并写入以下命令

#include<stdio.h>
int main(void)
{
	printf(" tong hiden 666\n");
	return 0;
}

之后make并可以得到第三个执行文件。此文件可以直接"./main"让他执行
如果此时我们不修改main就会被提示此文件未被更新
make: ‘main’ is up to date.
在这里插入图片描述

2.认识标识符

以下几个麻烦一直记着!!!!!!!!因为用多了就就会了,不用麻烦了

$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 代表第一个依赖文件

我们先把我们刚刚的Makefike改成以下内容

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
 
ALL: main1
main1: $(OBJ)
	@echo "action  2 "
	@echo "object file is " $(OBJ)
	@echo "some dependent file is " $<
	@echo "object file is " $@
	gcc $< -o $@
 
%.o: %.c
	@echo "action  1 "
	@echo "all_dependent file is " $^
	@echo "some dependent file is " $<
	@echo "object file is "  $@
	gcc -c $< -o $@
clean:
	rm main.o

看到这里是不是很懵逼,为啥一大堆符合出现了的时候突然就可以编译成新的可执行文件“main1”了,我的main.c呢??这就是Makefile的魅力!!!
SRC = $(wildcard *.c) ,这句话是加载当前路径的所有.c文件
@echo $(OBJ) 打印某个变量
OBJ = $(patsubst %.c, %.o, $(SRC)) 这句话是加载当前SRC的所包含有.c文件,并不改源文件情况下重命名.c文件成.o并赋值给OBJ,"不改源文件情况下重命名.c"啥意思呢。相当于如下图c语言中的变量dd,至于我们为啥要拥有.o。看看编译流程
在这里插入图片描述

$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 代表第一个依赖文件

然后我们回头看此章节中的那几个符号,根据我们打印的就会发现,
在action 1中。因为我们只有一个源文件,那么 $^ 与 $<都是main.c,$@是main.o。
在action 2中。(忽略第一行打印,备注 错的 )因为我们只有一个源文件就是 main.o,那么 $<都是main.0,$@是main1。

那么我们会问,啥时候给这几个符号赋值的呢?为啥他的顺序不从上到下?-------其实是执行make的时候并不是立马从上到下执行,他先会遍历一遍,相当于我们编译过程中的预处理–去掉宏,然后给对应的数据赋值,根据编译规则,他会发现编译main1的依赖文件是
OBJ = $(patsubst %.c, %.o, $(SRC)) 展开后的 mian.o,而在gcc $< -o $@中 $< 是依赖文件mian.o,所以赋值,而后mian.o没有。其会去索引%,去寻找需要在action 2中的依赖文件mian.o,纵使action 2是编译最终可执行文件的命令

刚刚说到了wildcard 和 patsubst ,就解释下我常用的几个函数
$(wildcard .source) :加载当前路径的所有.source文件
$(patsubst %.from ,%.to, $(/sourcedir)) :模式替换函数 并不改源文件情况下从/sourcedir中重命名.from 文件成.to 并返回
( s u b s t . f r o m , . t o , (subst .from, .to , (subst.from.to(/sourcedir)) :字符串替换函数并不改源文件情况下从/sourcedir中重命名.from 文件成.to 并返回
上面两个是不是感觉很像,subst 是 遇到匹配的字符就会 比如奇葩文件是 aaa.from.from,那么他都会aaa .to .to ,但是putsubst的话则会aaa.from.to
( f i l t e r (filter %.c .d, (filter(source))函数名称:过滤函数过滤掉其他的只用.c .d后缀的。
( f o r e a c h i t e m , (foreach item , (foreachitem,(sorcename), -I ( i t e m ) ) f o r e a c h 遍历函数读取 s o r c e n a m e 里面用空格分隔的成员重新定义成 − I 成员名并返回等同于 (item)) foreach 遍历函数读取sorcename里面用空格分隔的成员重新定义成 -I成员名并返回 等同于 (item))foreach遍历函数读取sorcename里面用空格分隔的成员重新定义成I成员名并返回等同于(sorcename:%=-I%)
$(notdir $(source)) 将source中的文件的路径名字去掉
$(basename $(source)) 将source中的基础名字的后缀去掉

lib_srcs:=$(filter-out src/main.c, $(wildcard  src/*.c))
lib_objs := $(patsubst src/%.c, obj/%.o,$(lib_srcs))

include_path := $(wildcard src/*.h)
include_path:=$(include_path:%=-I%)

lib_path:=./lib
link_libs:=mymath
lib_link_l:=$(link_libs:%=-l%)
lib_link_L:=$(lib_path:%=-L%)


compile_flags:= -g -O3 $(include_path)
link_flags:=  $(lib_link_L) $(lib_link_l)

debug:
	@echo $(link_flags)

obj/%.o:src/%.c
	mkdir -p $(dir $@)
	gcc -c $^ -o $@ $(compile_flags)

lib/libmymath.a:$(lib_objs)
	mkdir -p $(dir $@)
	ar -r $@ $^
makelib:lib/libmymath.a
obj/main.o:main.c
	mkdir -p $(dir $@)
	gcc -c $^ -o $@
exec:obj/main.o
	gcc -o $@ $^ $(compile_flags) $(link_flags)
execmake :exec
.PHONY:debug
lib_src:=$(wildcard src/*.c)
lib_obj:=$(patsubst src/%.c,obj/%.o,$(lib_src))

lib_include:= $(wildcard src/*.h)
lib_include:= $(lib_include:%=-I%)

lib_path:=./lib
lib_name :=mymath

link_name:=$(lib_name:%=-l%)

link_path:=$(lib_path:%=-L%)

run_link:=$(lib_path:%=-Wl,-rapth=%)


link_flag:=$(link_path) $(link_name) $(run_link) 

compile_flag:= -g -O3 -w -fPIC $(lib_include)

debug:
	echo $(link_flag)

obj/%.o:src/%.c
	mkdir -p $(dir $@)
	gcc -c $^ -o $@  $(compile_flag)
obj/%.o:src/%.c
	mkdir -p $(dir $@)
	gcc -c $^ -o $@ $(compile_flags)
makeo:$(lib_obj)
lib/libmymath.so:$(lib_obj)
	mkdir -p $(dir $@)
	gcc  -shared $^ -o $@ 
makelib:lib/libmymath.so
main:main.c
	gcc $^ -o $@  $(compile_flags)  $(link_flag) 

make命令执行过程

看不懂函数符号都给我去看这个笔者的博客,对于Makefile的函数比较详细

3.多级目录编译

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值