4通用Makefile编写

摘要:
dependencies中如果有一个以上的文件时间要比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。在Makefile中的命令,必须要以[Tab]键开始。使用=定义的变量不能追加新值,使用:=定义的变量可以使用+=追加新值2.3文件指示在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。

a.c

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

intmain()
{
    
    printf("hello world
");
    printf("A= %d
",A);
    test_fun();
    return 0;
}

a.h

#define A 1

b.c

#include <stdio.h>

inttest_fun()
{ 
    printf("it is B
");
    return 0;
}

编译test_Makefile的方法:
a. gcc -o test a.c b.c
对于a.c: 预处理、编译、汇编
对于b.c:预处理、编译、汇编
最后链接
优点:命令简单
缺点:如果文件很多,即使你只修改了一个文件,但是所有的文件文件都要重新"预处理、编译、汇编"
效率低

b. 写Makefile
核心:规则

目标:依赖1 依赖2
命令

命令执行的条件:
i. "依赖"文件 比 "目标"文件 新
ii.没有"目标"这个文件
这是一个文件的依赖关系,也就是说,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_1

test: a.c b.c a.h 
    gcc -o test a.c b.c

只要有任一文件变化,就执行gcc -o test a.c b.c效率低

Makefile_2

test : a.o b.o 
    gcc -o test a.o b.o
a.o : a.c
    gcc -c -o a.o a.c
b.o : b.c
    gcc -c -o b.o b.c

文件不会随着某个文件变化而全部编译

Makefile_3

test : a.o b.o 
    gcc -o test a.o b.o
    
a.o : a.c a.h

%.o : %.c
    gcc -c -o $@ $<

加通通配符,通配符%,$@表示目标值,$<表示第一个依赖,$^表示所有依赖

不能生成依赖文件

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

编译过程:

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

电子书Makefile

CROSS_COMPILE = arm-linux-AS        = $(CROSS_COMPILE)asLD        =$(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 
export STRIP OBJCOPY OBJDUMP
编译选项
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
链接选项
LDFLAGS := -lm -lfreetype

export CFLAGS LDFLAGS

假目标

PHONY :=__build
__build:

obj-y :=subdir-y :=
包含当前目录下的Makefile
include 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分为3类:
1. 顶层目录的Makefile
2. 顶层目录的Makefile.build
3. 各级子目录的Makefile

一、各级子目录的Makefile:
它最简单,形式如下:
obj-y += file.o
obj-y += subdir/
"obj-y += file.o"表示把当前目录下的file.c编进程序里,
"obj-y += subdir/"表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。

注意: "subdir/"中的斜杠"/"不可省略

二、顶层目录的Makefile:
它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,主要是定义工具链、编译参数、链接参数──就是文件中用export导出的各变量。

三、顶层目录的Makefile.build:
这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o
详细的讲解请看视频。

四、怎么使用这套Makefile:
1.把顶层Makefile, Makefile.build放入程序的顶层目录
2.修改顶层Makefile
2.1 修改工具链
2.2 修改编译选项、链接选项
2.3 修改obj-y决定顶层目录下哪些文件、哪些子目录被编进程序
2.4 修改TARGET,这是用来指定编译出来的程序的名字

3. 在各一个子目录下都建一个Makefile,形式为:
obj-y += file1.o
obj-y += file2.o
obj-y += subdir1/
obj-y += subdir2/

4. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除

免责声明:文章转载自《4通用Makefile编写》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇在vue 项目中嵌入jsp页面kudu 知识点学习(一)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用gpg来加密数据

一、数据的加密方式 数据加密有三种方式: 1、对称加密(算法有:DES、AES、3DES、)加密和解密使用同一个密钥 2、非对称加密(RSA、DSA、ELGamal等等)一共四把钥匙,用公钥加密数据,只能使用与之配对的私钥解密;反之亦然 3、单项加密(md5  sha1 sha2 sha128 sha256 sha512等)算出数据的hash值,当数据发生...

VSCode:如何对搜索结果同时选中并编辑

使用快捷键:CTRL+SHIFT+L 示例:我们有下面多条链接,现在我们需要将链接中的181、182...数字找出来,并替换成110; http://test.baidu.com/paper/test/181/90689.html http://test.baidu.com/paper/test/182/90688.html http://test.bai...

Shell学习(五)Shell输出命令

一、echo命令 #1、直接显示字符串 echo "It is a test" #输出:It is a test #2、显示转义字符 echo ""It is a test"" #输出:"It is a test" #3、显示变量 name=Shell echo "$name It is a test" #输出:Shell is a test #4、...

pcre 使用

1、主页地址:http://www.pcre.org/下载pcre-7.8.tar.bz22、解压缩:tar xjpf pcre-7.8.tar.bz23、配置:cd pcre-7.8./configure --prefix=/usr/local/pcre-7.8 --libdir=/usr/local/lib/pcre --includedir=/usr...

配置文件入门 WebConfig.config常用配置节点介绍

  一、配置文件入门   .Net提供了一种保存项目配置信息的办法,就是利用配置文件,配置文件的后缀一般是.config。在WinForm程序中配置文件一般是App.config。在Asp.net中一般默认是web.config。   一个.config配置文件都是基于XML的文本文件,并且可以保存到Web应用程序中的任何目录中。在发布Web应用程序时we...

阿里巴巴移动安全应用加固能力养成记

近些年来,移动APP数量呈现爆炸式的增长,黑产也从原来的PC端转移到了移动端,伴随而来的逆向攻击手段也越来越高明。在解决加固产品容易被脱壳的方案中,代码混淆技术是对抗逆向攻击最有效的方式之一。但目前的移动端加固技术真能抵御黑客的攻击吗? 本报告将分享阿里巴巴集团安全部应用加固能力养成记,重点介绍Android加固对于端上的业务风险控制是如何做到自动化部署...