您现在的位置是:首页 > 正文

makefile 学习笔记(一)

2024-02-01 05:43:18阅读 2

edit : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o /
           insert.o search.o files.o utils.o

objects = main.o kbd.o command.o display.o
insert.osearch.o files.o utils.o
edit : ( o b j e c t s ) / (objects) / (objects)/表示毒地变量的引用
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么
whatever.c,就会是shater.o的依赖文件。并且cc -c whatever 也会被推导出来。
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
rm edit $(objects)
这种方法,也就是make的“隐晦规则”。上面文件内容,“.PHONY”表示,clean是个未目标文件。

这种风格,让我们的makefile变得很简单,单我们的文件依赖关系就显得有点凌乱了。
这种风格不是太好,一是问价你的依赖关系看不清楚,二是如果文件一多,要加入几个新的.o文件,那就理不清楚了。
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o

edit : $(objects)
cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
rm edit $(objects)

Makefile里主要包含五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

1、显式规则。显式规则说明了,如何生成一个或多的的目标文件。
这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。

2、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地
简略地书写Makefile,这是由make所支持的。

3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,
当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;
另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。

5、 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。
如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。

最后,还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。 没有看懂

在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。
include foo.make *.mk $(bar)
include foo.make a.mk b.mk c.mk e.mk f.mk

1.如果make执行时,有“-I”或“–include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。
2.如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。

-include
其表示,无论include过程中出现什么错误,都不要报错继续执行。

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。

而只有一个命令“cc -c -g foo.c”(以Tab键开头)。这个规则告诉我们两件事:

一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。

make支持三各通配符:“*”,“?”和“[…]”。这是和Unix的B-Shell是相同的。

“~”
波浪号(“”)字符在文件名中也有比较特殊的用途。如果是“/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。

文件搜索

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,
你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。

Makefile文件中的特殊变量“VPATH”就是完成这个功能的,
如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:…/headers

另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,
这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。
它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。

vapth使用方法中的< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。< pattern>指定了要搜索的文件集,而< directories>则指定了的文件集的搜索的目录。例如:

vpath %.h …/headers 该语句表示,要求make在“…/headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)

最早先的一个例子中,我们提到过一个“clean”的目标,这是一个“伪目标”,
“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

个目标的生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自动化变量“$@”(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。

bigoutput littleoutput : text.g

       generate text.g -$(subst output,,$@) > $@

上述规则等价于:

bigoutput : text.g

       generate text.g -big > bigoutput

littleoutput : text.g

       generate text.g -little > littleoutput

其中,- ( s u b s t o u t p u t , , (subst output,, (substoutput,,@)中的“ ” 表 示 执 行 一 个 M a k e f i l e 的 函 数 , 函 数 名 为 s u b s t , 后 面 的 为 参 数 。 关 于 函 数 , 将 在 后 面 讲 述 。 这 里 的 这 个 函 数 是 截 取 字 符 串 的 意 思 , “ ”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。 这里的这个函数是截取字符串的意思,“ Makefilesubst@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。

<targets…>: : <prereq-patterns …>

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

如果我们的定义成“%.o”,意思是我们的集合中都是以“.o”结尾的
如果我们的定义成“%.c”,意思是对所形成的目标集进行二次定义
其计算方法是,取模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c

       $(CC) -c $(CFLAGS) $< -o $@

指明了我们的目标从 o b j e c t 中 获 取 , “ 而 命 令 中 的 “ object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”, 而命令中的“ object<”和“ @ ” 则 是 自 动 化 变 量 , “ @”则是自动化变量,“ @<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也褪恰癴oo.o bar.o”)。

files = foo.elc bar.o lose.o

( f i l t e r (filter %.o, (filter(files)): %.o: %.c

       $(CC) -c $(CFLAGS) $< -o $@

( f i l t e r (filter %.elc, (filter(files)): %.elc: %.el

       emacs -f batch-byte-compile $<

自动生成依赖性

main.o : main.c defs.h
大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
cc -M main.c
其输出是:
main.o : main.c defs.h

如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c

       @set -e; rm -f $@; \                  //这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm-f $@”的意思是删除所有的目标,也就是[.d]文件,

        $(CC) -M $(CPPFLAGS) $< > $@.    //第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“

; \

        sed 's,

.o[ :]*,\1.o $@ : ,g’ < $@.

$@; \

        rm -f $@.

我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过)

sources = foo.c bar.c
include ( s o u r c e s : . c = . d ) 上 述 语 句 中 的 “ (sources:.c=.d) 上述语句中的“ (sources:.c=.d)(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,
在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标

通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,

要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。
比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。
示例一:

   exec:

           cd /home/hchen

           pwd

示例二:

   exec:

           cd /home/hchen; pwd

当我们执行“make exec”时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,

而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen”。

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。
如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。

为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

clean:

       -rm -f *.o

subsystem:

       cd subdir && $(MAKE)

subsystem:

       $(MAKE) -C subdir

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。
这两个例子的意思都是先进入“subdir”目录,然后执行make命令。

  示例一:

    export variable = value

其等价于:

    variable = value

    export variable



   其等价于:

    export variable := value

    其等价于:



   variable := value

   export variable

示例二:

   export variable += value



   其等价于:



   variable += value

   export variable

subsystem:

       cd subdir && $(MAKE) MAKEFLAGS=

如果你定义了环境变量MAKEFLAGS,那么你得确信其中的选项是大家都会用到的,如果其中有“-t”,“-n”,和“-q”参数,那么将会有让你意想不到的结果,或许会让你异常地恐慌。

一、变量的基础
有一些变量是很奇怪字串,如“ < ” 、 “ <”、“ <@”等,这些是自动化变量,我会在后面介绍。

objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)

$(objects) : defs.h

变量会在使用它的地方精确地展开,就像C/C++中的宏一样,例如:

foo = c
prog.o : prog.$(foo)
( f o o ) (foo) (foo)(foo) - ( f o o ) p r o g . (foo) prog. (foo)prog.(foo)

展开后得到:

prog.o : prog.c
cc -c prog.c

变量在声明时需要给予初值,而在使用时,需要给在变量名前 加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。

二、变量中的变量

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
echo $(foo)

CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar

当“CFLAGS”在命令中被展开时,会是“-Ifoo -Ibar -O”。但这种形式也有不好的地方
,那就是递归定义,如:

CFLAGS = $(CFLAGS) -O
或:

A = $(B)
B = $(A)

为了避免上面的这种方法,我们可以使用make中的另一种用变量来定义变量的方法。
这种方法使用的是“:=”操作符,如:

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

其等价于:

y := foo bar
x := later

y := $(x) bar
x := foo
那么,y的值是“bar”,而不是“foo bar”。

ifeq (0,${MAKELEVEL})
cur-dir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := M A K E h o s t − t y p e = {MAKE} host-type= MAKEhosttype={host-type} whoami=${whoami}
endif

nullstring :=
space := $(nullstring) # end of the line

nullstring 是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。

dir := /foo/bar # directory to put the frobs in
dir这个变量的值是“/foo/bar”,后面还跟了4个空格,如果我们这样使用这样变量来指定别的目录——“$(dir)/file”那么就完蛋了。

FOO ?= bar
其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif

$(var:a=b)
${var:a=b}
把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。

foo := a.o b.o c.o
bar := ( f o o : 这 依 赖 于 被 替 换 字 串 中 的 有 相 同 的 模 式 , 模 式 中 必 须 包 含 一 个 “ (foo:%.o=%.c) 这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同样让 (foo:(bar)变量的值为“a.c b.c c.c”。

x = y
y = z
a := ( ( ((x))

在这个例子中, ( x ) 的 值 是 “ y ” , 所 以 (x)的值是“y”,所以 (x)y( ( x ) ) 就 是 (x))就是 (x))(y),于是 ( a ) 的 值 就 是 “ z ” 。 ( 注 意 , 是 “ x = y ” , 而 不 是 “ x = (a)的值就是“z”。(注意,是“x=y”,而不是“x= (a)zx=yx=(y)”)

a := ( ( ((x))
:= $(y)
:= z

x = y
y = z
z = u
a := ( ( (($(x)))

这里的$(a)的值是“u”

x = $(y)
y = z
z = Hello
a := ( ( ((x))

a := ( ( ((x))
:= $(z)
:= Hello

x = variable1
variable2 := Hello
y = ( s u b s t 1 , 2 , (subst 1,2, (subst1,2,(x))
z = y
a := ( ( (($(z)))

最终, ( a ) 的 值 就 是 (a)的值就是 (a)(variable2)的值—— “Hello”。

a := ( ( (($(z)))
:= ( ( ((subst 1,2,$(x)))
:= ( ( ((subst 1,2,variable1))
subst函数把“variable1”中的所有“1”字串替换成“2”字串
:= ( ( ((subst 1,2,variable1))
:= $(variable2)
:= Hello

网站文章

  • uboot 编译出现/bin/sh: 1: arm-linux-gnueabi-gcc: not found 问题

    出现此问题的原因可能是编译命令 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j8。此命令中 arm-linux-gnueabi- -j8 没有正确的路径地址 将绝对路径添加上可能就好了。

    2024-02-01 05:43:10
  • HCIP课程笔记-08-3/4/5/7类LSA、OSPF特殊区域

    HCIP课程笔记-08-3/4/5/7类LSA、OSPF特殊区域

    HCIP课程笔记-08-3/4/5/7类LSA、OSPF特殊区域

    2024-02-01 05:43:03
  • 接口幂等性问题处理

    接口幂等性问题处理

    文章目录一、接口幂等性概念1. 接口调用存在的问题2. 什么是接口幂等性3. 什么情况下需要保证接口的幂等性二、那些情况需要防止任意多次执行所产生的影响均与一次执行的影响相同,这是幂等性的核心特点。其...

    2024-02-01 05:42:35
  • 【数据结构和算法】全面剖析树的各类遍历方法 热门推荐

    【数据结构和算法】全面剖析树的各类遍历方法 热门推荐

    面试中常考到树的前序,中序,后序和层序遍历,这篇博文就带你深度剖析一下二叉树的各类遍历算法的实现二叉树的遍历主要有四种,前序、中序、后序和层序遍历的实现方式主要是:递归和非递归递归遍历的实现非常容易,非递归的实现需要用到栈,难度系数要高一点。一、二叉树节点的定义二叉树的每个节点由节点值、左子树和右子树组成。class TreeNode{ public: int val; TreeNo

    2024-02-01 05:42:28
  • Mac查看本地 MySQL版本信息

    Mac查看本地 MySQL版本信息

    键入安装mysql时设置的密码 即可看到version等信息。

    2024-02-01 05:42:24
  • MySQL常用的七种join查询

    MySQL常用的七种join查询

    目录 一、INNER JION 内连接 ( A ∩ B ) 二、LEFT JOIN 左外连接( A 全有 ) 三、RIGHT JOIN 右外连接 (B 全有) ​四、FULL JOIN 全外连接( A + B)  五、LEFT Excluding JOIN  ( A - B 即 A 表独有) 六、RIGHT Excluding JOIN ( B...

    2024-02-01 05:41:55
  • XXX银行项目部署

    XXX银行项目部署 一、下载项目代码 1、使用git工具下载代码 代码路径:推荐代码下载到桌面 git clone http://sunyard_姓名拼音@bitbucket.devops.hfdev/scm/zyc/rm-code.git 2、下载完成,桌面自动生成rm-code目录 3、选中桌面rm-code,右击鼠标,选中...

    2024-02-01 05:41:48
  • IDEA插件的存储位置及编程实现

    本文介绍了IDEA插件的存储位置,并提供了通过编程查找和操作这些插件的示例代码。无论您使用的是Windows还是macOS,都可以根据相应的存储位置找到已安装的插件,并对其进行管理。本文将介绍IDEA...

    2024-02-01 05:41:40
  • rabbitmq代码

    文章目录1.生产者代码2.消费者代码2.1 消费幂等性代码2.2 消费者rpc代码2.3 消费者消费重试2.4 消费者直接交换机代码2.5 基础代码 1.生产者代码

    2024-02-01 05:41:31
  • C语言-----格式字符、整型、字符型、浮点型

    C语言-----格式字符、整型、字符型、浮点型

    1、C语言格式字符:%d、%c、%s、%f、%lf等字符格式详解。2、C语言数据类型-----整型、字符型、浮点型详解。3、C语言转义符:\n、\t、\b、\r、\a等转义符详解。

    2024-02-01 05:40:55