Makefile:= ?= += =的区别

在Makefile中我们经常看到 = := ?= +=这几个赋值运算符,那么他们有什么区别呢?我们来做个简单的实验

新建一个Makefile,内容为:

ifdef DEFINE_VRE

VRE = “Hello World!”

else

endif

ifeq ($(OPT),define)

VRE ?= “Hello World! First!”

endif

ifeq ($(OPT),add)

VRE += “Kelly!”

endif

ifeq ($(OPT),recover)

VRE := “Hello World! Again!”

endif

all:

@echo $(VRE)

敲入以下make命令:

make DEFINE_VRE=true OPT=define 输出:Hello World!

make DEFINE_VRE=true OPT=add 输出:Hello World! Kelly!

make DEFINE_VRE=true OPT=recover  输出:Hello World! Again!

make DEFINE_VRE= OPT=define 输出:Hello World! First!

make DEFINE_VRE= OPT=add 输出:Kelly!

make DEFINE_VRE= OPT=recover 输出:Hello World! Again!

从上面的结果中我们可以清楚的看到他们的区别了

= 是最基本的赋值

:= 是覆盖之前的值

?= 是如果没有被赋值过就赋予等号后面的值

+= 是添加等号后面的值

之前一直纠结makefile中“=”和“:=”的区别到底有什么区别,因为给变量赋值时,两个符号都在使用。网上搜了一下,有人给出了解答,但是本人愚钝,看不懂什么意思。几寻无果之下,也就放下了。今天看一篇博客,无意中发现作者对于这个问题做了很好的解答。解决问题之余不免感叹,有时候给个例子不就清楚了吗?为什么非要说得那么学术呢。^_^

1、“=”

make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:

x = foo
            y = $(x) bar
            x = xyz

在上例中,y的值将会是 xyz bar ,而不是 foo bar 。

2、“:=”

“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

x := foo
            y := $(x) bar
            x := xyz

在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。

makefile宏定义:EXTRA_CFLAGS += -DCONFIG_ =y

EXTRA_CFLAGS += -D 与CONFIG_ =y

1.

假如定义一个宏CONFIG_DEBUG

在.c里面定义为:#define CONFIG_DEBUG

在makefile里定义为: CONFIG_DEBUG=y

假如说我们想在makefile里为.c文件进入一个宏定义,就用EXTRA_CFLAGS += DCONFIG_DEBUG(等价于在.c文件里定义#define CONFIG_DEBUG)

这时CONFIG_DEBUG=y与EXTRA_CFLAGS += DCONFIG_DEBUG的区别应该你已经看出来的,前者是对makefile编译时用的,比如说obj-(CONFIG_DEBUG) += test.o,而后者则是对.c源文件里的用的,比如说:

#if defined(CONFIG_DEBUG)

...

#else

...

#endif

2.

假如定义一个宏CONFIG_DEBUG = 3

在.c里面定义为:#define CONFIG_DEBUG 3

假如说我们想在makefile里为.c文件进入一个宏定义,就用EXTRA_CFLAGS += -DCONFIG_DEBUG=3

此时两者的定义完全相同。

Makefile中通配符*%的区别是什么?

此两者均为通配符,但更准确的讲,%为Makefile规则通配符,一般用于规则描述,如

%.o:%c

$(CC)  $< -o $@

表示所有的目标文件及其依赖文件,或者

$(filter %.c ,SOURCES)

此处SOURCES表示包含.c .cc .cpp等多类型源文件,该过滤器函数将c文件过滤出来,而%.c即为此过滤器规则。

通配符*则不具备上述功能。尤其是在Makefile,当变量定义或者函数调用时,该通配符的展开功能就失效了,即不能正常使用了,此时需要借助wildcard函数。二者应用范围不同。

Makefile有三个非常有用的变量。分别是$@$^$<代表的意义分别是:

# 这是上面那个程序的Makefile文件:

main:main.o mytool1.o mytool2.o

gcc -o main main.o mytool1.o mytool2.o

main.o:main.c mytool1.h mytool2.h

gcc -c main.c

mytool1.o:mytool1.c mytool1.h

gcc -c mytool1.c

mytool2.o:mytool2.c mytool2.h

gcc -c mytool2.c

有了这个Makefile文件,不论我们什么时候修改了源程序当中的什么文件,我们只要执行make命令,我们的编译器都只会去编译和我们修改的文件有关的文件,其它的文件它连理都不想去理的。 下面我们学习Makefile是如何编写的。 在Makefile中也#开始的行都是注释行.Makefile中最重要的是描述文件的依赖关系的说明。一般的格式是:

target:components

TAB rule

第一行表示的是依赖关系。第二行是规则。 

比如说我们上面的那个Makefile文件的第二行。

main:main.o mytool1.o mytool2.o

表示我们的目标(target)main的依赖对象(components)是main.o mytool1.omytool2.o当倚赖的对象在目标修改后修改的话,就要去执行规则一行所指定的命令。

就象我们的上面那个Makefile第三行所说的一样要执行 gcc-o main main.o mytool1.o mytool2.o注意规则一行中的TAB表示那里是一个TAB键 Makefile有三个非常有用的变量。

分别是$@,$^,$<代表的意义分别是:

$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。 如果我们使用上面三个变量,

那么我们可以简化我们的Makefile文件为:

# 这是简化后的Makefile

main:main.o mytool1.o mytool2.o

gcc -o $@ $^

main.o:main.c mytool1.h mytool2.h

gcc -c $<

mytool1.o:mytool1.c mytool1.h

gcc -c $<

mytool2.o:mytool2.c mytool2.h

gcc -c $<

经过简化后,我们的Makefile是简单了一点,不过人们有时候还想简单一点。这里我们学习一个Makefile的缺省规则 .c.o: gcc -c $< 这个规则表示所有的 .o文件都是依赖与相应的.c文件的。例如mytool.o依赖于mytool.c这样Makefile还可以变为:

# 这是再一次简化后的Makefile

main:main.o mytool1.o mytool2.o

gcc -o $@ $^

.c.o:

gcc -c $<

好了,我们的Makefile 也差不多了,如果想知道更多的关于Makefile的规则,可以查看相应的文档。

makefilePHONY的相关介绍

PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字。有两种理由需要使用PHONY目标:避免和同名文件冲突,改善性能。

如果编写一个规则,并不产生目标文件,则其命令在每次make该目标时都执行。例如:
  clean:
  rm *.o temp
因为"rm"命令并不产生"clean"文件,则每次执行"make clean"的时候,该命令都会执行。如果目录中出现了"clean"文件,则规则失效了:没有依赖文件,文件"clean"始终是最新的,命令永远不会执行;为避免这个问题,可使用".PHONY"指明该目标。如:
  .PHONY : clean
  这样执行"make clean"会无视"clean"文件存在与否。

已知phony 目标并非是由其它文件生成的实际文件,make会跳过隐含规则搜索。这就是声明phony目标会改善性能的原因,即使你并不担心实际文件存在与否。
  完整的例子如下:
  .PHONY : clean
  clean :
  rm *.o temp

phony 目标可以有依赖关系。当一个目录中有多个程序,将其放在一个makefile中会更方便。因为缺省目标是makefile中的第一个目标,通常将这个phony目标叫做"all",其依赖文件为各个程序:
  all : prog1 prog2 prog3
  .PHONY : all
  prog1 : prog1.o utils.o
           cc -o prog1 prog1.o utils.o
  prog2 : prog2.o
           cc -o prog2 prog2.o
  prog3 : prog3.o sort.o utils.o
           cc -o prog3 prog3.o sort.o utils.o

假设你的一个项目最后需要产生两个可执行文件。你的主要目标是产生两个可执行文件,但这两个文件是相互独立的——如果一个文件需要重建,并不影响另一个。你可以使用“假象目的”来达到这种效果。一个假象目的跟一个正常的目的几乎是一样的,只是这个目的文件是不存在的。因此, make总是会假设它需要 被生成,当把它的依赖文件更新后,就会执行它的规则里的命令行。

如果在我们的 makefile 开始处输入:

all : exec1 exec2

其中 exec1 和 exec2是我们做为目的的两个可执行文件。 make把这个 'all' 做为它的主要目的,每次执行时都会尝试把 'all'更新。但既然这行规则里没有哪个命令来作用在一个叫 'all'的实际文件(事实上 all并不会在磁碟上实际产生),所以这个规则并不真的改变 'all'的状态。可既然这个文件并不存在,所以 make会尝试更新 all 规则,因此就检查它的依靠 exec1, exec2是否需要更新,如果需要,就把它们更新,从而达到我们的目的。

假象目的也可以用来描述一组非预设的动作。例如,你想把所有由 make产生的文件删除,你可以在 makefile里设立这样一个规则:

veryclean :
rm *.o
rm myprog

前提是没有其它的规则依靠这个 'veryclean'目的,它将永远不会被执行。但是,如果你明确的使用命令 'make veryclean', make会把这个目的做为它的主要目标,执行那些 rm命令。

如果你的磁碟上存在一个叫 veryclean文件,会发生什么事?这时因为在这个规则里没有任何依靠文件,所以这个目的文件一定是最新的了(所有的依靠文件都已经是最新的了),所以既使用户明确命令 make重新产生它,也不会有任何事情发生。解决方法是标明所有的假象目的(用 .PHONY),这就告诉 make不用检查它们是否存在于磁碟上,也不用查找任何隐含规则,直接假设指定的目的需要被更新。在 makefile里加入下面这行包含上面规则的规则:

.PHONY : veryclean

就可以了。注意,这是一个特殊的 make规则,make知道 .PHONY是一个特殊目的,当然你可以在它的依靠里加入你想用的任何假象目的,而 make知道它们都是假象目的。

Makefile与shell脚本区别

在Makefile可以调用shell脚本,但是Makefile和shell脚本是不同的。本文试着归纳一下Makefile和shell脚本的不同。
1、 shell中所有引用以$打头的变量其后要加{},而在Makefile中的变量是以$打头的后加()。实例如下:
Makefile
PATH="/data/"
SUBPATH=$(PATH)

Shell
PATH="/data/"
SUBPATH=${PATH}

2、Makefile中所有以$打头的单词都会被解释成Makefile中的变量。如果你需要调用shell中的变量(或者正则表达式中锚定句位$),都需要加两个$符号($$)。实例如下:

PATH="/data/"

all:
    echo ${PATH}
    echo $$PATH

例子中的第一个${PATH}引用的是Makefile中的变量,而不是shell中的PATH环境变量,后者引用的事Shell中的PATH环境变量。

3、通配符区别
shell 中通配符*表示所有的字符
Makefile 中通配符%表示所有的字符

4、在Makefile中只能在某一个target下的命令中调用Shell脚本,其他地方是不能输出的。比如如下代码就是没有任何输出:

VAR="Hello"
echo "$VAR"

all:
   .....以上代码任何时候都不会输出,没有在任何target下得命令内,如果上述代码改为如下:

VAR="Hello"

all:
    echo "$VAR"
    .....以上代码,在make all的时候将会执行echo命令。

5、在Makefile中执行shell命令,一行创建一个进程来执行。这也是为什么很多Makefile中有很多行的末尾都是“;  \”,以此来保证代码是一行而不是多行,这样Makefile可以在一个进程中执行,例如:

SUBDIR=src example
all:
    @for subdir in $(SUBDIR); \
    do\
        echo "building "; \
    done上述可以看出for循环中每行都是以”; \”结尾的。

6、获取当前目录

PATH=`pwd` 注意是``,不是’'

Makefile中的shell2009-12-24 09:27:05

分类:

一下摘录Makefile中调用shell的一段

install:

-if [ ! -e xxx ]; then sudo mkdir xxx; fi

注意,将上面的if语句写到一行的话,必须在fi前面加上分号,否则会出现下面错误

unexpected end of file

下面转一个相关文章

MakefileShell的问题

大概只要知道Makefile的人,都知道Makefile可以调用Shell脚本。但是在实际使用时,并不那么简单,一些模棱两可的地方可能会让你抓狂。你若不信,可以先看几个例子,想象一下这些这些例子会打印什么内容,记下你想象的结果,然后在计算机上运行这些例子,对照看一下。

示例一:

if [ "$(BUILD)" = "debug" ]; then  echo "build debug"; else echo "build release"; fi

all:

echo "done"

示例二:

all:

@CC=arm-linux-gcc

@echo $(CC)

示例三:

CC=arm-linux-gcc

all:

@echo $(CC)

示例四:

SUBDIR=src example

all:

@for subdir in $(SUBDIR); \

do\

echo "building " $(subdir); \

done

说明:

1.         Shell脚本在target里才有效,其它地方都被忽略掉了。所以示例一中,”build debug”之类的字符串根本打印不出来。示例一的正确写法是:

示例一:

all:

if [ "$(BUILD)" = "debug" ]; then  echo "build debug"; else echo "build release"; fi

echo "done"

2.         make把每一行Shell脚本当作一个独立的单元,它们在单独的进程中运行。示例二中,两行Shell脚本在两个莫不相干的进程里运行,第一个进程把CC设置为arm-linux-gcc,第二个进程是不知道的,所以打印的结果自然不是arm-linux-gcc了。示例二的正确写法是:

示例二:

all:

@CC=arm-linux-gcc; echo $(CC)

或者:

all:

@CC=arm-linux-gcc; \

echo $(CC)

3.         make在调用Shell之前先进行预处理,即展开所有Makefile的变量和函数。这些变量和函数都以$开头。示例三中,Shell拿的脚本实际上是echo arm-linux-gcc,所以打印结果正确。

4.         make预处理时,所有以$开头的,它都不会放过。要想引用Shell自己的变量,应该以$$开头。另外要注意,Shell自己的变量是不需要括号的。示例四的正确写法是:

示例四:

SUBDIR=src example

all:

@for subdir in $(SUBDIR); \

do\

echo "building " $$subdir; \

done

感谢,Thanks!

linux Makefile obj-m obj-y ..

分类: Linux

2013-02-20 14:01 1773人阅读 评论(0) 收藏 举报

目标定义是Kbuild Makefile的主要部分,也是核心部分。主要是定义了要编 译的文件,所有的选项,以及到哪些子目录去执行递归操作。 最简单的Kbuild makefile 只包含一行: 例子: obj-y += foo.o 该例子告诉Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c 或foo.S文件编译得到。 如果foo.o要编译成一模块,那就要用obj-m了。所采用的形式如下: 例子: obj-$(CONFIG_FOO) += foo.o $(CONFIG_FOO)可以为y(编译进内核) 或m(编译成模块)。如果CONFIG_FOO不是y 和m,那么该文件就不会被编译联接了

Linux各级内核源代码的子目录下都有Makefile,大多数Makefile要嵌入主目录下的Rule.make,Rule.make将识别各个Makefile中所定义的一些变量。变量obj-y表示需要编绎到内核中的目标文件名集合,定义O_TARGET表示将obj-y连接为一个O_TARGET名称的目标文件,定义L_TARGET表示将obj-y合并为一个L_TARGET名称的库文件。同样obj-m表示需要编绎成模块的目标文件名集合。如果还需进行子目录make,则需要定义subdir-y和subdir-m。在Makefile中,用"obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o"和"subdir-$(CONFIG_EXT2_FS) += ext2"这种形式自动为obj-y、obj-m、subdir-y、subdir-m添加文件名。有时,情况没有这么单纯,还需要使用条件语句个别对待。Makefile中还有其它一些变量,如mod-subdirs定义了subdir-m以外的所有模块子目录。

Rules.make是如何使make进入子目录的呢? 先来看subdir-y是如何处理的,在Rules.make中,先对subdir-y中的每一个文件名加上前缀"_subdir_"再进行排序生成subdir-list集合,再以它作为目标集,对其中每一个目标产生一个子make,同时将目标名的前缀去掉得到子目录名,作为子make的起始目录参数。subdir-m与subdir-y类似,但情况稍微复杂一些。由于subdir-y中可能有模块定义,因此利用mod-subdirs变量将subdir-y中模块目录提取出来,再与subdir-m合成一个大的MOD_SUB_DIRS集合。subdir-m的目标所用的前缀是"_modsubdir_"。

一点说明,子目录中的Makefile与Rules.make都没有嵌入.config文件,它是通过主Makefile向下传递MAKEFILES变量完成的。MAKEFILES是make自已识别的一个变量,在执行新的Makefile之前,make会首先加载MAKEFILES所指的文件。在主Makefile中它即指向.config。

Makefile札记相关推荐

  1. 浅显易懂 Makefile 入门 (12)— Makefile 常见的错误信息

    1. 常见的错误信息 make 执行过程中所产生错误并不都是致命的,特别是在命令行之前存在 -.或者 make 使用 -k 选项执行时. make 执行过程的致命错误都带有前缀字符串 ***.错误信息 ...

  2. 浅显易懂 Makefile 入门 (10)— 嵌套执行 make、export 的使用

    1. 嵌套执行 make 在一个大的工程文件中,不同的文件按照功能被划分到不同的模块中,每个模块可能都会有自己的编译顺序和规则,如果在一个 Makefile 文件中描述所有模块的编译规则,就会很乱,执 ...

  3. 浅显易懂 Makefile 入门 (09)— include 文件包含、MAKECMDGOALS

    1. include文件包含 当 make 读取到 include 关键字的时候,会暂停读取当前的 Makefile,而是去读 include 包含的文件,读取结束后再继读取当前的 Makefile ...

  4. 浅显易懂 Makefile 入门 (08)— 默认 shell (/bin/sh)、命令回显、make参数(-n 只显示命令但不执行,-s 禁止所有回显)、单行命令、多行命令、并发执行

    1. shell 相关 1.1 默认 shell Makefile 所使用的命令是由 shell 命令行组成,他们是一条一条执行的. 多个命令之间要使用分号隔开,Makefile 中的任何命令都要以 ...

  5. 浅显易懂 Makefile 入门 (07)— 其它函数(foreach 、if、call、origin )

    1. foreach 函数 foreach 函数定义如下: $(foreach <var>,<list>,<text>) 函数的功能是:把参数 <list&g ...

  6. 浅显易懂 Makefile 入门 (06)— 文件名操作函数(dir、notdir、suffix、basename、addsuffix、addperfix、join、wildcard)

    编写 Makefile 的时候,很多情况下需要对文件名进行操作.例如获取文件的路径,去除文件的路径,取出文件前缀或后缀等等. 注意:下面的每个函数的参数字符串都会被当作或是一个系列的文件名来看待. 1 ...

  7. 浅显易懂 Makefile 入门 (03)— 目标文件搜索(VPATH 和 vpath 的区别和使用)、隐含规则

    1. 目标文件搜索(VPATH和vpath) 如果需要的文件是存在于不同的路径下(即源文件与 Makefile 文件不在同一个路径下),在编译的时候就用到了 Makefile 中为我们提供的目录搜索文 ...

  8. 浅显易懂 Makefile 入门 (02)— 普通变量和自动变量定义、使用($@、$^、$< 作用)、变量覆盖 override、变量的来源 origin

    1. 变量的定义 Makefile 文件中定义变量的基本语法如下: 变量的名称=值列表 变量的名称可以由大小写字母.阿拉伯数字和下划线构成.等号左右的空白符没有明确的要求,因为在执行 make 的时候 ...

  9. 浅显易懂 Makefile 入门 (01)— 什么是Makefile、为什么要用Makefile、Makefile规则、Makefile流程如何实现增量编译

    1. 什么是 Makefile Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目.一旦写编写好 Makefile 文件,只需要一个 ma ...

最新文章

  1. 畅通工程 HDU - 1233 
  2. java+map对象判断空值_java判断map中是否存在指定对象
  3. leetcode151. 翻转字符串里的单词
  4. Python小游戏-接苹果
  5. caas k8s主控节点如何查询_k8s中部署prometheus监控告警系统prometheus系列文章第一篇...
  6. 如何将txt文件转为EXCEL文件?
  7. modbus-tcp协议通过Java代码获取从机数据
  8. 解决Navicat 15注册机出现 rsa public key not found
  9. ML.NET 示例:聚类之客户细分
  10. 回顾总结-----第九届中国云计算大会,量子计算机为最大亮点
  11. js删除网页中图片width 和 height
  12. C# 读写Excel
  13. Surface pro 4 使用心得
  14. 【已解决】office提示你的许可证不是正版,你可能是盗版软件的受害者?
  15. 平面设计中的字体设计新花样
  16. java olap oracle_【案例】Oracle数据库升级OLAP组件异常 删除OLAP组件
  17. TI FMCW毫米波雷达基础(3)——角度测量原理
  18. C# SuperSocket 手把手教你入门 傻瓜教程---1(服务器单向接收客户端发送数据)
  19. Chrome 和 Firefox 版本100对User-Agent的缓冲处理
  20. 故障处理 软件 需求_一分钟了解软件测试类型

热门文章

  1. Guava之RangeMap
  2. 【线性代数公开课MIT Linear Algebra】 第二十三课 微分方程与exp(At)
  3. (转)rvm安装与常用命令
  4. java中的codereview
  5. js unix时间戳转换
  6. 为移动端网页构造快速响应按钮
  7. 2012.7.24---C#(2)
  8. 测试Rockey 4 Smart加密锁的C语言代码
  9. 计算机硬件系统的构成教学设计,2.1 计算机硬件系统教学设计思路
  10. android linux截图库,Android中截图(surfaceView)源码