嵌入式Linux应用开发基础知识(六)——Makefile实例

本文通过实例展示了如何完善Makefile,包括解决头文件依赖问题、利用GCC选项自动生成依赖关系,以及CFLAGS的应用,如-Werror和-Idir。详细讲解了M选项、MM选项和MG选项的作用,以及如何结合它们创建更高效的编译流程。
摘要由CSDN通过智能技术生成

前面我们学了很多Makefile相关的知识,但是没有写过一个完整的代码,这一章我们写出一个实例

一、完善Makefile

在之前我们写了一个较为完善的Makefile程序,但是还是存在一些问题,我们需要慢慢完善程序。相关程序如下:
main.c

#include <stdio.h>
#include "sub.h"

int main(int argc, char *argv[])
{

       int i;
       printf("Main fun!\n");
       sub_fun();
       return 0;
}

sub.c

#include "sub.h"

void sub_fun(void)
{
       printf("Sub fun! A=%d\n", A);
}

sub.h

#define A    2

void sub_fun(void);

Makefile

test : main.o sub.o
        gcc -o test $^
%.o : %.c
        gcc -c -o $@ $<
clean:
        rm *.o test

.PHONY: clean

运行结果:
在这里插入图片描述
由上述程序可知,sub.c中使用了A的宏命令,我们在sub.h改变A的值,再次编译:
在这里插入图片描述
发现提示:test没有更新,这是为什么呢?
这是因为我们在Makefile中没有依赖sub.h,无论sub.h 怎么变化,sub.o都不会改变,导致test没有更新,那么我们应该在Makefile中加入依赖代码,如下:

test : main.o sub.o
        gcc -o test $^
sub.o : sub.c sub.h
%.o : %.c
        gcc -c -o $@ $<
clean:
        rm *.o test

.PHONY: clean

当A改为3时,运行结果为:
在这里插入图片描述

那么每次新增了头文件,是不是得我们手动添加呢?显然这样是不高效的,C/C++的编译器提供了自动获取头文件依赖的方法。

二、自动获取头文件的依赖关系

1. 基础知识

1.1 -M选项

GCC的-M选项生成文件的依赖关系,同时也把一些标准库的头文件包含了进来。本质是告诉预处理器输出一个适合 make 的规则,用于描述各目标文件的依赖关系。对于每个源文件,预处理器输出 一个 make 规则,该规则的目标项 (target) 是源文件对应的目标文件名,依赖项 (dependency) 是源文件中 “#include” 引用的所有文件,生成的规则可以是单行,但如果太长,就用’'换行符续成多行。规则显示在标准输出,不产生预处理过的 C 程序。注意:该选项默认打开了 -E 选项, -E 参数的用处是使得编译器在预处理结束时就停止编译。
例如,执行下面的命令:

gcc -M main.c

输出如下:
在这里插入图片描述
由编译器自动生成依赖关系,这样做的好处有以下几点:

  • 不必手动书写若干目标文件的依赖关系,由编译器自动生成
  • 不管是源文件还是头文件有更新,目标文件都会重新编译

1.2 -MM选项

生成文件的依赖关系,和 -M 类似,但不包含标准库的头文件
例如:
···
gcc -MM main.c
···
输出结果:

1.3 -MG选项

要求把缺失的头文件按存在对待,并且假定他们和源文件在同一目录下,必须和 ‘-M’ 选项一起用。

1.4 -MF File

必须和 ‘-M’ 或’-MM’选项一起用,当使用了 “-M” 或者 “-MM” 选项时,则把依赖关系写入名为 “File” 的文件中。若同时也使用了 “-MD” 或 “-MMD”,“-MF” 将覆写输出的依赖文件的名称 。
例如:

 gcc -M -MF main.d main.c

输出结果:
在这里插入图片描述
则 “-M” 输出的内容就保存在 main.d 文件中了。

1.5 -MD或者-MMD

等同于 -M -MF File(或者 -MM -MF File),但是默认关闭了 -E 选项。其输出的文件名是基于 -o 选项,若给定了 -o 选项,则输出的文件名是 -o 指定的文件名,并添加 .d 后缀,若没有给定,则输入的文件名作为输出的文件名,并添加 .d 后缀,同时继续指定的编译工作。
注意:-MD 不会像 -M 那样阻止正常的编译任务,因为它默认关闭了 -E 选项,比如命令中使用了 -c 选项,其结果要生成 .o 文件,若使用了 -M 选项,则不会生成 .o 文件,若使用的是 -MD 选项,则会生成 .o 文件,有没有关闭-E,只要看窗口是否输出编译信息。
例如:

gcc -E -MD main.c

输出:
在这里插入图片描述
本目录下生成了以下文件:
main.d
同时在终端上输出了 main.c 文件的预处理结果

例如:

gcc -E -o tmp.i -MD main.c

本目录下生成了以下文件:
tmp.d tmp.i

例如:

gcc -c -MD main.c

本目录下生成了以下文件:
main.d main.o

例如:

gcc -c -o tmp.o -MD main.c

本目录下生成了以下文件:
tmp.d tmp.o

2. 继续优化Makefile

如果我们想编译生成main.o的同时且把依赖写入到main.d,代码如下:

gcc -c -o main.o main.c -MD -MF main.d

输出如下:
在这里插入图片描述

我们继续优化Makefile,一般.d文件可以不用被用户看到,我们设置为隐藏(Linux如果要将一个文件隐藏,只需在前面加上. , 此时使用ls,或者ls -l查看不到,只能使用ls -a才能查看到)
优化后的Makefile如下:


test : main.o sub.o
        gcc -o test $^
sub.o : sub.c sub.h
%.o : %.c
        gcc -c -o $@ $< -MD -MF .$@.d
clean:
        rm *.o test

.PHONY: clean

运行结果图:
在这里插入图片描述
可以看到生成我们需要的 .main.o.d .sub.o.d文件

3. 比较完善的Makefile

实例代码如下:


objs = main.o sub.o

dep_files := $(patsubst %, .%.d, $(objs))
dep_files := $(wildcard $(dep_files))

test : $(objs)
        gcc -o test $^

ifneq ($(dep_files),)
include $(dep_files)
endif

%.o : %.c
        gcc -c -o $@ $< -MD -MF .$@.d
clean:
        rm *.o test

distclean:
        rm $(dep_files)
.PHONY: clean

运行结果:
在这里插入图片描述
代码解释:

  • 为了方便书写依赖的对象文件,程序使用objs 即时变量存储OBJ文件
  • dep_files := $(patsubst %, .%.d, $(objs))这段代码是将main.o sub.o变为.main.o.d .sub.o.d
  • test : $(objs) 这是直接使用objs对象进行编译
  • ifneq endif是Makefile的if条件编译语句
  • ifneq ($(dep_files),)表示如果dep_files为空,则条件为真,逗号后面有啥都没有表示空
  • include 语句和C语言的include语句类似,都是包含相应的头文件,使其编译
  • distclean标签用于清除.x.o.d文件

三、CFLAGS选项

CFLAGS 表示用于 C 编译器的选项,可以搭配各种选项达到不同的作用,现在我们介绍两种选项

1. -Werror 选项

该选项表示将所有警告当做错误进行处理,在程序开发时,建议采用这种方式,因为很多错误是由于警告引起来的,警告我们也不能忽视,如下为添加该选项后的Makefile文件代码


objs = main.o sub.o

dep_files := $(patsubst %, .%.d, $(objs))
dep_files := $(wildcard $(dep_files))

CFLAGS = -Werror

test : $(objs)
        gcc -o test $^

ifneq ($(dep_files),)
include $(dep_files)
endif

%.o : %.c
        gcc -c -o $@ $< -MD -MF .$@.d
clean:
        rm *.o test

distclean:
        rm $(dep_files)
.PHONY: clean

2. -Idir 选项

-Idir选项可以指定编译器查找头文件的路径,加入这个选项后,我们用户定义的头文件就可以使用尖括号进行包含了,我们在根目录下新建include文件夹,并把所有的头文件放在这里,则Makefile就可以这么写了:


objs = main.o sub.o

dep_files := $(patsubst %, .%.d, $(objs))
dep_files := $(wildcard $(dep_files))

CFLAGS = -Werror -Iinclude

test : $(objs)
        gcc -o test $^

ifneq ($(dep_files),)
include $(dep_files)
endif

%.o : %.c
        gcc -c -o $@ $< -MD -MF .$@.d
clean:
        rm *.o test

distclean:
        rm $(dep_files)
.PHONY: clean

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
############################################################################# # Makefile for building: sample 2011-09-26 # # Project: # Template: # Command: # ------基本上简单用法的makefile------- #1. 第一个目标为最终目标 #2. 命令以 Tab开头,可以有多个命令 #3. 分行号\ 后面不可以跟空格 #4、加@可以去掉命令显示 #5. 变量为 abc = efd 访问为 $(abc) echo $abc # # # #缺点,单文件夹 #每次都会重新生成 # #foo.o : foo.c defs.h # foo模块 #cc -c -g foo.c # #多目录 一种方法,在主目录里面include "",然后其里面OBJS += .o,这样其实就是 或用foreach ############################################################################# #target EXECUTABLE := test CC := gcc CXX := g++ STRIP := strip AR := ar cqs LINK := g++ RM := rm -f CFLAGS := -g -Wall CXXFLAGS := $(CFLAGS) CXXFLAGS += -MD LIBS := -lm LIBPATH := -L/usr/local/lib INCPATH := ####### Output directory OBJSPATH := ../Obj/ EXECUTABLEPATH := ../Execute/ #######source Files SOURCE := $(wildcard *.c) $(wildcard *.cpp) OBJS := $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(SOURCE))) DEPS := $(patsubst %.o,%.d,$(OBJS)) #######rule .SUFFIXES: .cpp .c .o .so .a .d $(OBJSPATH)%.o:%.c $(CC) $(CFLAGS) -c $< -o $@ $(OBJSPATH)%.o:%.cpp $(CXX) $(CXXFLAGS) -c $< -o $@ $(OBJSPATH)%.d:%.cpp $(CXX) -MM $ $@ ######main .PHONY : all deps objs clean rebuild all: $(EXECUTABLE) $(CXX) $(CXXFLAGS) $(INCLUDEPATH) $(LIBS) $(LIBPATH) $(addprefix $(OBJSPATH),$(OBJS)) \ -o $(EXECUTABLEPATH)$(EXECUTABLE) deps: $(addprefix $(OBJSPATH),$(DEPS)) objs: $(addprefix $(OBJSPATH),$(OBJS)) clean: @$(RM) $(OBJSPATH)*.o @$(RM) $(OBJSPATH)*.d @$(RM) $(EXECUTABLEPATH)$(EXECUTABLE) rebuild: clean all -include $(addprefix $(OBJSPATH),$(DEPS)) ##.d里面是详细的.o rule 自己会括展开的,然后没有文件就自己去重建 $(EXECUTABLE) : objs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零涂

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值