一个通用的Makefile

简介: 一 makefile的作用     Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中记录有文件的信 息,在make时会决定在链接的时候需要重新编译哪些文件。

一 makefile的作用

    Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中记录有文件的信 息,在make时会决定在链接的时候需要重新编译哪些文件。Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动发现最终的生成文件已经过时,而应该重新编译相应的模块。 makefile带来的好处就是—"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。默认的情况下,make命令会在当前目录下按顺序找寻文件名为"GNUmakefile"、"makefile"、"Makefile"的文件,找到了解释这个文件。当然也可以使用make -f DIR/makefile 来指定用于makefile文件

二 makefile语法简述

2.1 makefile规则

target ... : dependencies ... 
            command

target目标文件,可以是Object File,也可以是执行文件,还可以是一个标签 
dependencies就是,要生成那个target所需要的文件或是目标。 
command也就是make需要执行的命令。(任意的Shell命令,可以有若干行)

    这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于dependencies中的文件,其生成规则定义在command中。dependencies 中如果有一个以上的文件时间要比target文件要新的话,command所定义的命令就会被执行。这就是 Makefile的规则。也就是Makefile中最核心的内容。在Makefile中的命令,必须要以[Tab]键开始。

2.2 变量定义

    Makefile中变量的定义一般有两种: =和:=。 =符号定义的变量叫延时变量,只有在使用的时候才扩展开来; :=符号定义的变量为立即变量,一旦定义就扩展。 使用=定义的变量不能追加新值,使用:=定义的变量可以使用+=追加新值

2.3 文件指示

    在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:include <filename> filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

2.4 伪目标

    伪目标并不是一个文件,只是一个标签,由于伪目标不是文件,所以make无法生成它的依赖关系和决定 它是否要执行。我们只有通过显示地指明这个目标才能让其生效。当然,伪目标的取名不能和文件名重名,不然其就失去了伪目标的意义了。当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记.PHONY来显示地指明一个目标是伪目标,向make说明,不管是否有这个文件,这个目标就是伪目标。

2.5 自动化变量

    $<    第一个依赖文件的名称

    $?    所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚

    $@    目标的完整名称

    $^    所有的依赖文件,以空格分开,不包含重复的依赖文件

 三 通用的makefile实例

工程必备:

顶层Makefile 顶层Makefile.build 子目录Makefile

编译过程:

    从顶层开始递归进入子目录,当进入到一个目录的最底层时,开始使用GCC编译,再将该层的所有.o文件打包成build-in.o,返回它的上一层目录再递归进入子目录,当编译完所有的子目录后,就开始编译顶层的.c文件,最后将顶层的.o文件和顶层每个子目录的build-in.o链接成我们的目标文件

顶层Makefile解析(随工程而变):

#----------------------------------------------指定编译工具链---------------------------------------------------

CROSS_COMPILE =                             #指定编译器种类

AS        = $(CROSS_COMPILE)as         #

LD        = $(CROSS_COMPILE)ld          #链接工具

CC        = $(CROSS_COMPILE)gcc       #编译工具

CPP        = $(CC) -E                             #

AR        = $(CROSS_COMPILE)ar         #打包工具

NM        = $(CROSS_COMPILE)nm       #

 

STRIP        = $(CROSS_COMPILE)strip              #优化工具

OBJCOPY        = $(CROSS_COMPILE)objcopy   #

OBJDUMP        = $(CROSS_COMPILE)objdump  #

 

export AS LD CC CPP AR NM                           #将定义的变量导出,方便其他makefile使用

export STRIP OBJCOPY OBJDUMP                   #将定义的变量导出,方便其他makefile使用

 

CFLAGS := -Wall -O2 -g                                    #编译器参数

CFLAGS += -I $(shell pwd)/include                     #指定编译器头文件(根据实际项目手动修改)

LDFLAGS := -lm -lfreetype -lvga                         #指定编译器链接库(根据实际项目手动修改)

export CFLAGS LDFLAGS                                #将定义的变量导出,方便其他makefile使用

TOPDIR := $(shell pwd)                                     #获得当前程序的顶层目录

export TOPDIR                                                 #输出顶层目录

 TARGET := show_file                                      #编译后的程序名(根据实际项目手动修改)

#-------------------------顶层要生成的.o文件以及顶层文件夹(根据实际项目手动修改)------------------

obj-y += main.o

obj-y += display/

obj-y += draw/

obj-y += encoding/

obj-y += fonts/

#--------------------------------------------顶层的第一个规则(默认规则)-----------------------------------------

all :

    make -C ./ -f $(TOPDIR)/Makefile.build           #进入当前目录,使用顶层的makefile.build进行编译

    $(CC) $(LDFLAGS) -o $(TARGET) built-in.o    #将编译好的built-in.o文件链接生成我们的目标文件

#------------------------------------------------顶层的清除规则-------------------------------------------------------

clean:

    rm -f $(shell find -name "*.o")                        #删除所有的.o文件

    rm -f $(shell find -name "*.d")                        #删除所有的.d文件

    rm -f $(TARGET)                                         #删除目标文件

.PHONY:all clean

顶层Makefile.build解析(无需改动):

PHONY := __build                                        #定义一个PHONY变量

__build:                                                       #开头说明__build伪目标,使其成为Makefile.build的第一个目标

obj-y :=                                                        #定义当前目录的目标变量,初始值为空

subdir-y :=                                                   #定义当前目录的子目录变量,初始值为空

include Makefile                                          #将当前目录的Makefile包含进来,初始化obj-y

                                                                   #obj-y:=a.o b.o c/ d/

__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))   #筛选出当前目录的目标变量中的子目录,并且去掉/

                                                                                   #$(filter %/, $(obj-y)):c/ d/

                                                                                   #__subdir-y:c d

subdir-y += $(__subdir-y)                                           #将开始定义的subdir-y赋值为__subdir-y

                                                                                   #subdir-y:c d

subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)  #对于subdir-y里面的每一个值(目录),增加一个相应的目录/built-in.o的变量值

                                                                                   #subdir_objs:c/built-in.o d/built-in.o

cur_objs := $(filter-out %/, $(obj-y))                            #得到obj-y中的.o文件

                                                                                   #cur_objs:a.o b.o

dep_files := $(foreach f,$(cur_objs),.$(f).d)                #对于所有的.o文件,定义它的依赖文件名

                                                                                   #dep_files: .a.d .b.d

dep_files := $(wildcard $(dep_files))

ifneq ($(dep_files),)                                                    #根据依赖文件名,判断依赖文件是否存在,存在就包含就来

    include $(dep_files)

endif

PHONY += $(subdir-y) #将$(subdir-y)也加入到变量PHONY中

--------------------------------------------Makefile. build的第一个规则--------------------------------------------------------------

__build : $(subdir-y) built-in.o                                    #第一个规则

$(subdir-y):                                                                #第一个规则的第一个依赖规则

    make -C $@ -f $(TOPDIR)/Makefile.build              #依次进入该子目录变量里面存储的值,使用的Makefile.build进行编译

 built-in.o : $(cur_objs) $(subdir_objs)                       #第一个规则的第二个依赖规则

      $(LD) -r -o $@ $^                                                 #该规则的命令:将该目录下的.o和$(subdir_obj)打包成built-in.o文件

dep_file = .$@.d                                                        #

%.o : %.c                                                                   #第一个规则的第二个依赖规则的依赖规则 

$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<#用于将目录下所有的.c文件编译成.o文件

.PHONY : $(PHONY)                                                  #将PHONY声明为伪目标

 子目录Makefile(随工程而变):

子目录的Makefile就是包含该目录下所有的目标文件名和子目录文件夹名。

例如任何一个子目录可写成:

objs-y := a.o

objs-y += b.o

objs-y += c/

objs-y += d/

四 示例工程

     http://pan.baidu.com/share/link?shareid=145262&uk=101680913

目录
相关文章
|
移动开发 小程序 JavaScript
开源的微信小程序框架
【8月更文挑战第22天】开源的微信小程序框架
681 65
|
10月前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
376 4
|
11月前
|
Python
如何使用正则表达式来匹配特定格式的电话号码?
如何使用正则表达式来匹配特定格式的电话号码?
|
机器学习/深度学习 人工智能 Java
《IDEA 2023常用插件安装与全局配置教程大全》——提升你的开发效率
《IDEA 2023常用插件安装与全局配置教程大全》——提升你的开发效率
1340 0
|
安全 Linux 数据库
windows服务器 cwRsyncServer 搭建步骤
windows服务器 cwRsyncServer 搭建步骤
1225 0
windows服务器 cwRsyncServer 搭建步骤
|
机器学习/深度学习 人工智能 算法
阿里云机器学习平台 PAI -推荐解决方案|学习笔记
快速学习阿里云机器学习平台 PAI -推荐解决方案。
1228 0
阿里云机器学习平台 PAI -推荐解决方案|学习笔记
|
弹性计算 人工智能 小程序
阿里云服务器租用价格表小程序(阿里云服务器租赁一般多少钱)
阿里云服务器租用价格表小程序(阿里云服务器租赁一般多少钱)阿里云轻量应用服务器2核2G3M带宽轻量服务器一年108元,2核4G4M带宽轻量服务器一年297.98元12个月;ECS云服务器e系列2核2G配置182元一年
585 0
|
弹性计算 Linux Windows
阿里云服务器更换操作系统操作流程及限制说明
阿里云服务器更换操作系统流程及Windows和Linux互换限制说明
1345 1
阿里云服务器更换操作系统操作流程及限制说明
|
人工智能 算法 安全
技术:Java-Web基础|生成图片验证码(二)
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类
技术:Java-Web基础|生成图片验证码(二)