【Linux】学习记录_5_Makefile

5 Makefile

Makefile中跟目标相关的语法:命令必须使用TAB缩进

[目标1]:[依赖]

​ [命令1]

​ [命令2]

[目标2]:[依赖]

​ [命令1]

​ [命令2]

  • 目标:指make要做的事情,可以是一个简单的代号,也可以是目标文件,需要顶格书写一个Makefile可以有多个目标,写在最前面的第一 个目标,会被Make程序确立为 “默认目标”。
  • 依赖:要达成目标需要依赖的某些文件或其它目标。在编译的例子中,hello_main依赖于hello_main.c、hello_func.c源文 件,若这些文件更新了会重新进行编译。
  • 命令1,命令2…命令n:make达成目标所需要的命令。只有当目标不存在或依赖文件的修改时间比目标文件还要新时,才会执行命令。要特别注意命令的开头要用“Tab”键,不能使用空格代替。

5.1 伪目标

在Makefile中编写的目标,在make看来其实都是目标文件,例如make在执行时由于在目录找不到targeta文件,所以每次make targeta的时候,它都会去执行targeta的命令,期待执行后能得到名为targeta的 同名文件。如果目录下真的有targeta、targetb、targetc的文件,即假如目标文件和依赖文件都存在且是最新的,那么make targeta就不会被正常执行了,这会引起误会。

Makefile使用“.PHONY”前缀来区分目标代号和目标文件,并且这种目标代号被称为“伪目标”。

.PHONY:clean
clean:
   rm -f *.o hello_main

GNU组织发布的软件工程代码的Makefile,常常会有类似以上代码中定义的clean伪目标, 用于清除编译的输出文件。常见的还有“all”、“install”、“print”、“tar”等 分别用于编译所有内容、安装已编译好的程序、列出被修改的文件及打包成tar文件。 虽然并没有固定的要求伪目标必须用这些名字,但可以参考这些习惯来编写自己的Makefile

5.2 默认规则

在前面《GCC编译过程》章节中提到整个编译过程包含如下图中的步骤,make在执行时也是 使用同样的流程,不过在Makefile的实际应用中,通常会把编译和最终的链接过程分开。

未找到图片05

hello_main目标文件本质上并不是依赖hello_main.c和hello_func.c文件, 而是依赖于hello_main.o和hello_func.o, 把这两个文件链接起来就能得到hello_main目标文件。 另外,由于make有一条默认规则,当找不到xxx.o文件时, 会查找目录下的同名xxx.c文件进行编译。

5.3 使用变量

使用C自动编译成 *.o的默认规则有个缺陷,由于没有显式地表示 *.o依赖于.h头文件, 假如我们修改了头文件的内容,那么 *.o并不会更新,这是不可接受的。 并且默认规则使用固定的“cc”进行编译,假如我们想使用ARM-GCC进行交叉编译, 那么系统默认的“cc”会导致编译错误。

要解决这些问题并且让Makefile变得更加通用,需要引入变量和分支进行处理。

5.3.1 基本语法

在Makefile中的变量,有点像C语言的宏定义,在引用变量的地方使用变量值进行替换。变量的命名可以包含字符、数字、下划线,区分大小写,定义变量的方式有以下四种:

  • “=” :延时赋值,该变量只有在调用的时候,才会被赋值
  • “:=” :直接赋值,与延时赋值相反,使用直接赋值的话,变量的值定义时就已经确定了。
  • “?=” :若变量的值为空,则进行赋值,通常用于设置默认值。
  • “+=” :追加赋值,可以往变量后面增加新的内容。
TARGET = hello_main    #定义变量
CC = gcc
CFLAGS = -I.
DEPS = hello_func.h
OBJS = hello_main.o hello_func.o

#目标文件
$(TARGET): $(OBJS)
   $(CC) -o $@ $^ $(CFLAGS)

#*.o文件的生成规则
%.o: %.c $(DEPS)
   $(CC) -c -o $@ $< $(CFLAGS)

#伪目标
.PHONY: clean
clean:
   rm -f *.o hello_main
5.3.2 其他自动化变量
符号意义
$@匹配目标文件
$% @ 类似,但 @类似,但 @类似,但%仅匹配“库”类型的目标文件
$<依赖中的第一个目标文件
$^所有的依赖目标,如果依赖中有重复的,只保留一份
$+所有的依赖目标,即使依赖中有重复的也原样保留
$?所有比目标要新的依赖目标

5.4 使用分支

ifeq(arg1, arg2)
分支1
else
分支2
endif

修改

#定义变量
#ARCH默认为x86,使用gcc编译器,
#否则使用arm编译器
ARCH ?= x86
TARGET = hello_main
CFLAGS = -I.
DEPS = hello_func.h
OBJS = hello_main.o hello_func.o

#根据输入的ARCH变量来选择编译器
#ARCH=x86,使用gcc
#ARCH=arm,使用arm-gcc
ifeq ($(ARCH),x86)
CC = gcc
else
CC = arm-linux-gnueabihf-gcc
endif

#目标文件
$(TARGET): $(OBJS)
   $(CC) -o $@ $^ $(CFLAGS)

#*.o文件的生成规则
%.o: %.c $(DEPS)
   $(CC) -c -o $@ $< $(CFLAGS)

#伪目标
.PHONY: clean
clean:
   rm -f *.o hello_main

5.5 最终Makefile

ARCH ?= x86
TARGET = hello_main

BUILD_DIR = build_$(ARCH)                        #存放中间文件的路径
SRC_DIR = $(shell find drv -maxdepth 3 -type d)    #当前目录下查找名为drv的文件夹,并在最多搜索3层深度内的子目录中查找。-type d选项指示find只返回目录(文件夹)而不是文件
INC_DIR = drv \                                #存放头文件的文件夹
drv/adc \
EX_FILES =

SRCS = $(filter-out ${EX_FILES}, $(foreach dir, $(SRC_DIRS), $(wildcard $(addprefix $(dir)/*, .c))))        #源文件
OBJS = $(addprefix $(BUILD_DIR)/,$(notdir $(SRC_FILES:.c=.o)))    #目标文件(*.o)
DEPS = $(wildcard $(INC_DIR)/*.h) #头文件

#指定头文件的路径
CFLAGS = $(patsubst %, -I%, $(INC_DIR))

#根据输入的ARCH变量来选择编译器
#ARCH=x86,使用gcc
#ARCH=arm,使用arm-gcc
ifeq ($(ARCH),x86)
CC = gcc
else
CC = arm-linux-gnueabihf-gcc
endif

#目标文件
$(BUILD_DIR)/$(TARGET): $(OBJS)
   $(CC) -o $@ $^ $(CFLAGS)

#*.o文件的生成规则
$(BUILD_DIR)/%.o: %.c $(DEPS)
    @mkdir -p $(BUILD_DIR)
    $(CC) -c -o $@ $< $(CFLAGS)

#伪目标
.PHONY: clean cleanall
#按架构删除
clean:
   rm -rf $(BUILD_DIR)

最终Makefile只做记录未经测试

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值