4通用Makefile编写
a.c
#include<stdio.h>
#include "a.h" int main()
{ printf("hello world\n");
printf("A= %d\n",A);
test_fun();
return 0;
}
a.h
#define A 1
b.c
#include <stdio.h> int test_fun()
{
printf("it is B\n");
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)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
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 :=
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编写的更多相关文章
- 一个通用Makefile的编写
作者:杨老师,华清远见嵌入式学院讲师. 我们在Linux环境下开发程序,少不了要自己编写Makefile,一个稍微大一些的工程下面都会包含很多.c的源文件.如果我们用gcc去一个一个编译每一个源文件的 ...
- 【linux】-Makefile简要知识+一个通用Makefile
目录 Makefile Makefile规则与示例 为什么需要Makefile Makefile样式 先介绍Makefile的两个函数 完善Makefile 通用Makefile的使用 通用的Make ...
- 一个简单的通用Makefile实现
一个简单的通用Makefile实现 Makefile是Linux下程序开发的自动化编译工具,一个好的Makefile应该准确的识别编译目标与源文件的依赖关系,并且有着高效的编译效率,即每次重新ma ...
- Yocto开发笔记之《Makefile编写》(QQ交流群:519230208)
开了一个交流群,欢迎爱好者和开发者一起交流,转载请注明出处. QQ群:519230208,为避免广告骚扰,申请时请注明 “开发者” 字样 =============================== ...
- Make和Makefile编写(详见GCC手册)
Makefile和Make Rules 多模块软件.依赖树和Make 默认规则 Make使用程序对简单变量的支持 内建变量 虚目标 特殊目标 一般性语法错误及其纠正措施 命令行的使用和调试 Makef ...
- linux 下C语言编程库文件处理与Makefile编写
做开发快3年了,在linux下编译安装软件算是家常便饭了.就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题.看来还是像vs.codeblocks这样的ide把人弄蠢了.便下定决心一 ...
- 通用Makefile
本文推荐了一个用于对 C/C++ 程序进行编译和连接以产生可执行程序的通用 Makefile. 在使用 Makefile 之前,只需对它进行一些简单的设置即可:而且一经设置,即使以后对源程序文件有所增 ...
- 单目录下多文件 makefile编写
makefile很久就接触过了,但是一直没怎么深入的去学习和总结:在项目中我也只是看看makefile或者修改部分语句,全部自己动手写的话还真没有:知识在于沉淀,这句说的非常好,所以现在把自己理解的东 ...
- linux --> Makefile编写
Makefile编写 单目录 测试程序在同一个文件中,共有func.h.func.c.main.c三个文件,Makefile写法如下所示: CC = gcc CFLAGS = -g -Wall mai ...
随机推荐
- TCP拥塞控制
TCP必须使用端到端拥塞控制而不是使网络辅助的拥塞控制,因为IP层不向端系统提供显式的网络拥塞反馈.TCP采用的方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率. 几个相关 ...
- SSH2 架构常用注解
1. @Repository 将 DAO 类声明为 Bean 2.@Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次. 3.@Service 通常作用在业务层 ...
- C语言中随机数相关问题
用C语言产生随机数重要用到rand函数.srand函数.及宏RAND_MAX(32767),它们均在stdlib.h中进行了声明. int rand(void);//生成一个随机数 voidsrand ...
- onclick="test()"与onclick="return test()"的区别
浏览器会对页面元素的某些操作产生默认行为比如a标签跳转,form表单的提交等如果是onclick="test()"则执行该函数,然后继续自己的默认行为 <a href=&qu ...
- js 数组的判断
<javascript语言精粹>中的 var is_array = function(value){ return value && //判断值是否为真,不接受null和其 ...
- C#引用Office.word出错的解决办法-无法嵌入互操作类型“Microsoft.Office.Interop.Word.ApplicationClass” 【转】
本文章转自 suchso 1.系统找不到 Microsoft.Office.Interop.Word" "Could not load file or assembly 'Micr ...
- hdu 3948 Portal (kusral+离线)
Portal Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Subm ...
- (转载)Android理解:显式和隐式Intent
Intent分两种:显式(Explicit intent)和隐式(Implicit intent). 一.显式(设置Component) 显式,即直接指定需要打开的activity对应的类. 以下多种 ...
- C#多态问题
为什么对这个感觉趣呢.因为以前写过两篇关于这个多态和重载混合起来很乱的调用情况分析,自从哪以后,我自认为随便乱写一些继承多态的代码都应该难不到我.但是今天看到一段代码有一个地方计算错误了,所以有必要写 ...
- 《Pro AngularJS》学习小结-01
<Pro AngularJS>该书以一个SportsStore案例为主线铺开. 一.开发环境设置 该书中所用的数据库data server开发环境是Deployed,从来没听说过,而且作者 ...