Makefile 随记
introduction-引言基础使用变量赋值及通配符常用函数
$(wildcard <指定格式>) 查找匹配格式$(patsubst %.c,%.o,main.c obj.c) 替换(notdir ./dir/main.c) 去除路径信息-I./inc 指定同文件$(addsuffix SUFFIX,NAMES...) 批量追加后缀include 包含其它文件$(foreach var,list,operate) 循环遍历+操作$(call operate,param1,param2,...) 自定义函数 内核Makefile
introduction-引言本文为本人学习Linux中Makefile时所记录的笔记,该笔记参考的B站或微信公众号有:韦东山、野火、正点原子、一口Linux、简说linux等。
Makefile文件是Linux环境下用记录并配置文件间依赖关系和编译规则的文件,一般该文件命名为Makefile,配置好后,在终端输入make命令便默认执行名为Makefile的文件,若要执行别名的文件则在终端中输入make -f <文件名>。
makefile代码的三要素为:目标文件、依赖文件、命令。
例1
#注释文本 test:main.o obj.o gcc main.o obj.o -o test main.o:main.c gcc main.c -o main.o obj.o:obj.c gcc obj.c -o obj.o .PHONY:clear clear: @rm -rf test
例1中从第一行开始,test为要生成的目标文件,main.o、obj.o为生成目标文件所需要的依赖文件,gcc main.o obj.o -o test为生成目标文件所要执行的命令。
当依赖文件不存在时,便会寻找依赖文件所需要的依赖,程序便先执行了main.o:main.c和obj.o:obj.c生成依赖文件,再执行第一行的代码。而.PHONY:clear是为了声明clear是一个伪目标,也就是不不需要依赖,相当与一条命令而已。输入make生成可执行文件test,输入make clear执行rm -rf test语句,删除test文件,@的作用是取消回显文本。
变量赋值及通配符一些在开发中常用的变量名:CC表示编译器,AS表示汇编,MAKE表示make工具,当然用户的变量名是随便取的,上述三个变量名仅是开发者命名的规范而已,变量的定义要放在最前面。
变量的通配符:
${变量名}:引用Makefile文件中变量$<:表示第一个依赖文件$^:表示所有的依赖文件$@:表示生成的目标文件$$:扩展打开Makefile中定义的shell变量
变量的赋值(3种):
延时赋值=:变量被引用时才开始赋值,立即赋值:=:变量会被立即赋值,无需等到被引用时空赋值?=:变量为空时才能赋值,一旦变量被赋值了,就无法用该符号赋值追加赋值+=:在原来的值末尾附加上新值。
例2:
CC=gcc
TARGET:=test
OBJS:=main.c obj.c
${TARGET}:${OBJS}
${CC} $^ -o $@
常用函数
$(wildcard <指定格式>) 查找匹配格式
列出所有符合指定格式的文件,可通过一些shell的一些通配符指定格式。
例3:列出所有.c结尾的文件
data:=$(wildcard *.c) #或 data:=$(wildcard %.c)
$(patsubst %.c,%.o,main.c obj.c) 替换
指定字符替换为另外的字符
例4:把含.c结尾的参数替换为.o结尾
data:=$(patsubst %.c,%.o,main.c obj.c) #把参数 main.c obj.c 替换为 main.o obj.o
(notdir ./dir/main.c) 去除路径信息
去除文件名中的路径信息。
例5:
DOC=( notdir ./dir/main.c ) #去除路径信息,只得到main.c
-I./inc 指定同文件
通过-I ./inc指定头文件路径为./inc
SRC=main.c
INC=-I./inc
test:main.c
gcc -c ${INC} ${SRC}
#gcc -c 编译汇编不连接,生成.o文件
$(addsuffix SUFFIX,NAMES…) 批量追加后缀
SUFFIX为后缀名,NAMES…为文件名
例6:
DOC:=$(addsuffix .c,text1 text2) #DOC=text1.c text2.c
include 包含其它文件
用法类似与C语言的#include "xxx.h"。
例7:
include file.h
$(foreach var,list,operate) 循环遍历+操作
list中的变量会被依次赋值到临时变量var,然后根据operate操作var,但list值读完后,函数返回一串operate后的变量。
注意:var在函数执行后将会释放,函数返回的是新的字符串,旧的字符串list不变。
例8
old := a b c d
new := $(foreach n,${old},${n}.o)
最后,新变量为:new=a.o b.o c.o d.o,而旧变量为:old=a b c d不变。
$(call operate,param1,param2,…) 自定义函数
把参数param1、param2传入到操作opeate中,并返回操作后的值。
例9
ope = $(2)$(1) ret = $(call ope,p1,p2) all: @echo "ret=$(ret)"
最后执行结果为ret=p2p1。
其中all表示默认目标,也可以理解为指定第一个依赖,起到指定程序开始执行位置的作用
内核Makefile
#B站 简说Linux视频源码,仅用于利用内核编译出.KO文件 ifneq($(KERNELRELEASE),) obj-m := helloDEV.O else PWD := $(shell pwd) KDIR := /home/linux-4.9.229 all: make -C $(KDIR) M=$(PWD) clean: rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~ endif
程序进入后,
- 执行ifneq($(KERNELRELEASE),),判断KERNELRELEASE是否为空,KERNELRELEASE是内核源码中的一个变量,第一次进入是为空。为空,则进入else部分,在else中,PWD变量获取当前编译的路径,KDIR获取内核源码的路径,然后执行all下的命令make -C $(KDIR) M=$(PWD),其中-C表示先进入到内核路径KDIR下先执行内核的Makefile文件,编译后,定义了KERNELRELEASE,且不为空,然后通过M=$(PWD)回到PWD变量指定的目录下执行该目录下的Makefile文件。再次进入该Makefile文件时,KERNELRELEASE不等空,执行obj-m := helloDEV.O,此语句用与内核的编译系统识别的*(内核编译系统:由内核源码中的众多Makefile文件组成)*。内核编译系统会把所有obj-m选项的文件编译成.KO文件。
韦东山例程编写的Makefile,它同时编译出了应用层的可执行文件*(通过arm-linux-gcc编译),和驱动程序.KO文件(主要通过obj-m += leddrv.o编译)*。
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR # 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: # 2.1 ARCH, 比如: export ARCH=arm64 # 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- # 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin # 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, # 请参考各开发板的高级用户使用手册 KERN_DIR = # 板子所用内核源码的目录 all: make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order rm -f ledtest # 参考内核源码drivers/char/ipmi/Makefile # 要想把a.c, b.c编译成ab.ko, 可以这样指定: # ab-y := a.o b.o # obj-m += ab.o obj-m += leddrv.o



