Makefile 随记

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.oobj.o为生成目标文件所需要的依赖文件,gcc main.o obj.o -o test为生成目标文件所要执行的命令。

当依赖文件不存在时,便会寻找依赖文件所需要的依赖,程序便先执行了main.o:main.cobj.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,…) 自定义函数

把参数param1param2传入到操作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

程序进入后,

  1. 执行ifneq($(KERNELRELEASE),),判断KERNELRELEASE是否为空,KERNELRELEASE是内核源码中的一个变量,第一次进入是为空。
  2. 为空,则进入else部分,在else中,PWD变量获取当前编译的路径,KDIR获取内核源码的路径,然后执行all下的命令make -C $(KDIR) M=$(PWD),其中-C表示先进入到内核路径KDIR下先执行内核的Makefile文件,编译后,定义了KERNELRELEASE,且不为空,然后通过M=$(PWD)回到PWD变量指定的目录下执行该目录下的Makefile文件。
  3. 再次进入该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

make -C

make -C <目录名>进入到制定目录名下并执行该目录下的makefile,执行结束后返回当前状态。

make -D

在make的过程中,往c语言工程中添加宏定义。

make -DMY_CONFIG main.c -o mian

那么在mian.c中可以通过#ifdef MY_CONFIG来执行配置相关的内容。

include

目前,我的理解就是简单的把包含的文件展开

在my.config中

MY_CONFIG=y

在makefile

include my.config
ifeq($(MY_CONFIG),y)
XXX
endif

中间的xxx是在相关配置的编译内容,比如:往工程中添加宏。

gcc -l选项

gcc -l<库文件路径>选项用于为编译器指定一个具体的库(库文件路径),l表示locate。

gcc -L

gcc -L<库文件路径>选项用于为编译器额外添加一个库

gcc -I(大写i)选项

gcc -L<头文件路径>选项用于为编译器额外添加头文件路径,I表示include。
注意:头文件和库文件是两种不同的文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值