到此为止,读者已经了解了如何在Linux下使用编辑器编写代码,如何使用Gcc把代码编译成可执行文件,还学习了如何使用Gdb来调试程序,那么,所有的工作看似已经完成了,为什么还需要Make这个工程管理器呢?

所谓工程管理器,顾名思义,是指管理较多的文件的。读者可以试想一下,有一个上百个文件的代码构成的项目,如果其中只有一个或少数几个文件进行了修改,按照之前所学的Gcc编译工具,就不得不把这所有的文件重新编译一遍,因为编译器并不知道哪些文件是最近更新的,而只知道需要包含这些文件才能把源代码编译成可执行文件,于是,程序员就不能不再重新输入数目如此庞大的文件名以完成最后的编译工作。

但是,请读者仔细回想一下本书在3.1.2节中所阐述的编译过程,编译过程是分为编译、汇编、链接不同阶段的,其中编译阶段仅检查语法错误以及函数与变量的声明是否正确声明了,在链接阶段则主要完成是函数链接和全局变量的链接。因此,那些没有改动的源代码根本不需要重新编译,而只要把它们重新链接进去就可以了。所以,人们就希望有一个工程管理器能够自动识别更新了的文件代码,同时又不需要重复输入冗长的命令行,这样,Make工程管理器也就应运而生了。

实际上,Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作。用户只需编写一次简单的编译语句就可以了。它大大提高了实际项目的工作效率,而且几乎所有Linux下的项目编程均会涉及到它,希望读者能够认真学习本节内容。

1  Makefile基本结构

Makefile是Make读入的惟一配置文件,因此本节的内容实际就是讲述Makefile的编写规则。在一个Makefile中通常包含如下内容:

·  需要由make工具创建的目标体(target),通常是目标文件或可执行文件;

·  要创建的目标体所依赖的文件(dependency_file);

·  创建每个目标体时需要运行的命令(command)。

它的格式为:

target: dependency_files

command

例如,有两个文件分别为hello.c和hello.h,创建的目标体为hello.o,执行的命令为gcc编译指令:gcc –c hello.c,那么,对应的Makefile就可以写为:

#The simplest example

hello.o: hello.c hello.h

gcc –c hello.c –o hello.o

接着就可以使用make了。使用make的格式为:make target,这样make就会自动读入Makefile(也可以是首字母小写makefile)并执行对应target的command语句,并会找到相应的依赖文件。如下所示:

[root@localhost makefile]# make hello.o

gcc –c hello.c –o hello.o

[root@localhost makefile]# ls

hello.c  hello.h  hello.o  Makefile

可以看到,Makefile执行了“hello.o”对应的命令语句,并生成了“hello.o”目标体。

注意

在Makefile中的每一个command前必须有“Tab”符,否则在运行make命令时会出错。

2  Makefile变量

上面示例的Makefile在实际中是几乎不存在的,因为它过于简单,仅包含两个文件和一个命令,在这种情况下完全不必要编写Makefile而只需在Shell中直接输入即可,在实际中使用的Makefile往往是包含很多的文件和命令的,这也是Makefile产生的原因。下面就可给出稍微复杂一些的Makefile进行讲解:

sunq:kang.o yul.o

Gcc kang.o bar.o -o myprog

kang.o : kang.c kang.h head.h

Gcc –Wall –O -g –c kang.c -o kang.o

yul.o : bar.c head.h

Gcc - Wall –O -g –c yul.c -o yul.o

在这个Makefile中有三个目标体(target),分别为sunq、kang.o和yul.o,其中第一个目标体的依赖文件就是后两个目标体。如果用户使用命令“make sunq”,则make管理器就是找到sunq目标体开始执行。

这时,make会自动检查相关文件的时间戳。首先,在检查“kang.o”、“yul.o”和“sunq”三个文件的时间戳之前,它会向下查找那些把“kang.o”或“yul.o”做为目标文件的时间戳。比如,“kang.o”的依赖文件为:“kang.c”、“kang.h”、“head.h”。如果这些文件中任何一个的时间戳比“kang.o”新,则命令“gcc –Wall –O -g –c kang.c -o kang.o”将会执行,从而更新文件“kang.o”。在更新完“kang.o”或“yul.o”之后,make会检查最初的“kang.o”、“yul.o”和“sunq”三个文件,只要文件“kang.o”或“yul.o”中的任比文件时间戳比“sunq”新,则第二行命令就会被执行。这样,make就完成了自动检查时间戳的工作,开始执行编译工作。这也就是Make工作的基本流程。

接下来,为了进一步简化编辑和维护Makefile,make允许在Makefile中创建和使用变量。变量是在Makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。在具体要求下,这些值可以代替目标体、依赖文件、命令以及makefile文件中其它部分。在Makefile中的变量定义有两种方式:一种是递归展开方式,另一种是简单方式。

递归展开方式定义的变量是在引用在该变量时进行替换的,即如果该变量包含了对其他变量的应用,则在引用该变量时一次性将内嵌的变量全部展开,虽然这种类型的变量能够很好地完成用户的指令,但是它也有严重的缺点,如不能在变量后追加内容(因为语句:CFLAGS = $(CFLAGS) -O在变量扩展过程中可能导致无穷循环)。

为了避免上述问题,简单扩展型变量的值在定义处展开,并且只展开一次,因此它不包含任何对其它变量的引用,从而消除变量的嵌套引用。

递归展开方式的定义格式为:VAR=var

简单扩展方式的定义格式为:VAR:=var

Make中的变量使用均使用格式为:$(VAR)

注意

变量名是不包括“:”、“#”、“=”结尾空格的任何字符串。同时,变量名中包含字母、数字以及下划线以外的情况应尽量避免,因为它们可能在将来被赋予特别的含义。

变量名是大小写敏感的,例如变量名“foo”、“FOO”、和“Foo”代表不同的变量。

推荐在makefile内部使用小写字母作为变量名,预留大写字母作为控制隐含规则参数或用户重载命令选项参数的变量名。

下面给出了上例中用变量替换修改后的Makefile,这里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。这样在以后修改时,就可以只修改变量定义,而不需要修改下面的定义实体,从而大大简化了Makefile维护的工作量。

经变量替换后的Makefile如下所示:

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $(OBJS) -o sunq

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c kang.c -o kang.o

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c yul.c -o yul.o

可以看到,此处变量是以递归展开方式定义的。

Makefile中的变量分为用户自定义变量、预定义变量、自动变量及环境变量。如上例中的OBJS就是用户自定义变量,自定义变量的值由用户自行设定,而预定义变量和自动变量为通常在Makefile都会出现的变量,其中部分有默认值,也就是常见的设定值,当然用户可以对其进行修改。

预定义变量包含了常见编译器、汇编器的名称及其编译选项。下表3.14列出了Makefile中常见预定义变量及其部分默认值。

表3.14                                                  Makefile中常见预定义变量

命 令 格 式

含    义

AR

库文件维护程序的名称,默认值为ar

AS

汇编程序的名称,默认值为as

CC

C编译器的名称,默认值为cc

CPP

C预编译器的名称,默认值为$(CC) –E

CXX

C++编译器的名称,默认值为g++

FC

FORTRAN编译器的名称,默认值为f77

RM

文件删除程序的名称,默认值为rm –f

ARFLAGS

库文件维护程序的选项,无默认值

ASFLAGS

汇编程序的选项,无默认值

CFLAGS

C编译器的选项,无默认值

CPPFLAGS

C预编译的选项,无默认值

CXXFLAGS

C++编译器的选项,无默认值

FFLAGS

FORTRAN编译器的选项,无默认值

可以看出,上例中的CC和CFLAGS是预定义变量,其中由于CC没有采用默认值,因此,需要把“CC=Gcc”明确列出来。

由于常见的Gcc编译语句中通常包含了目标文件和依赖文件,而这些文件在Makefile文件中目标体的一行已经有所体现,因此,为了进一步简化Makefile的编写,就引入了自动变量。自动变量通常可以代表编译语句中出现目标文件和依赖文件等,并且具有本地含义(即下一语句中出现的相同变量代表的是下一语句的目标文件和依赖文件)。下表3.15列出了Makefile中常见自动变量。

表3.15                                                    Makefile中常见自动变量

命令格式

含    义

$*

不包含扩展名的目标文件名称

$+

所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件

$<

第一个依赖文件的名称

$?

所有时间戳比目标文件晚的依赖文件,并以空格分开

命令格式

含    义

$@

目标文件的完整名称

$^

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

$%

如果目标是归档成员,则该变量表示目标的归档成员名称

自动变量的书写比较难记,但是在熟练了之后会非常的方便,请读者结合下例中的自动变量改写的Makefile进行记忆。

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

kang.o : kang.c kang.h

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

yul.o : yul.c yul.h

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

另外,在Makefile中还可以使用环境变量。使用环境变量的方法相对比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量。但是,如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。

3  Makefile规则

4  Make使用

使用make管理器非常简单,只需在make命令的后面键入目标名即可建立指定的目标,如果直接运行make,则建立Makefile中的第一个目标。

此外make还有丰富的命令行选项,可以完成各种不同的功能。下表3.17列出了常用的make命令行选项。

表3.17                                                        make的命令行选项

命令格式

含    义

-C dir

读入指定目录下的Makefile

-f file

读入当前目录下的file文件作为Makefile

命令格式

含    义

-i

忽略所有的命令执行错误

-I dir

指定被包含的Makefile所在目录

-n

只打印要执行的命令,但不执行这些命令

-p

显示make变量数据库和隐含规则

-s

在执行命令时不显示命令

-w

如果make在执行过程中改变目录,则打印当前目录名

Makefile的规则是Make进行处理的依据,它包括了目标体、依赖文件及其之间的命令语句。一般的,Makefile中的一条语句就是一个规则。在上面的例子中,都显示地指出了Makefile中的规则关系,如“$(CC) $(CFLAGS) -c $< -o $@”,但为了简化Makefile的编写,make还定义了隐式规则和模式规则,下面就分别对其进行讲解。

1.隐式规则

隐含规则能够告诉make怎样使用传统的技术完成任务,这样,当用户使用它们时就不必详细指定编译的具体细节,而只需把目标文件列出即可。Make会自动搜索隐式规则目录来确定如何生成目标文件。如上例就可以写成:

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

为什么可以省略后两句呢?因为Make的隐式规则指出:所有“.o”文件都可自动由“.c”文件使用命令“$(CC) $(CPPFLAGS) $(CFLAGS) -c file.c –o file.o”生成。这样“kang.o”和“yul.o”就会分别调用“$(CC) $(CFLAGS) -c kang.c -o kang.o”和“$(CC) $(CFLAGS) -c yul.c -o yul.o”生成。

注意

在隐式规则只能查找到相同文件名的不同后缀名文件,如”kang.o”文件必须由”kang.c”文件生成。

下表3.16给出了常见的隐式规则目录:

表3.16                                                Makefile中常见隐式规则目录

对应语言后缀名

规    则

C编译:.c变为.o

$(CC) –c $(CPPFLAGS) $(CFLAGS)

C++编译:.cc或.C变为.o

$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Pascal编译:.p变为.o

$(PC) -c $(PFLAGS)

Fortran编译:.r变为-o

$(FC) -c $(FFLAGS)

2.模式规则

模式规则是用来定义相同处理规则的多个文件的。它不同于隐式规则,隐式规则仅仅能够用make默认的变量来进行操作,而模式规则还能引入用户自定义变量,为多个文件建立相同的规则,从而简化Makefile的编写。

模式规则的格式类似于普通规则,这个规则中的相关文件前必须用“%”标明。使用模式规则修改后的Makefile的编写如下:

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

%.o : %.c

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

Make工程管理器(读书笔记)相关推荐

  1. linux的工程管理器是,Linux工程管理器——make

    一.定义 工程管理器,顾名思义,是指管理较多的文件 Make工程管理器也就是个"自动编译管理器",这里的"自动"是指它能构根据文件时间戳自动发现更新过的文件而减 ...

  2. Day 3 Linux(目录篇 、文件操作篇 、压缩解压相关命令, 编辑器vi 、编译器gcc 、调试器gdb、 makefile工程管理器初学)

    一.目录篇: 1.文件处理命令:cp 英文原意:copy 语法:cp -rp[源文件或目录][目的目录] 源文件可以同时是多个,即同时复制到多个文件 -r 复制目录 -p保留文件属性,比如文件创建时间 ...

  3. 《Objective-C高级编程 iOS与OS X多线程和内存管理》读书笔记

    <Objective-C高级编程 iOS与OS X多线程和内存管理>读书笔记 第一章:自动引用计数 自己生成的对象,自己所持有. 非自己生成的对象,自己也能持有 不再需要自己持有的对象时释 ...

  4. Delphi Project 之工程管理器(Project Manager)

    工程(Project)可以说是Delphi的生命.不可能创建一个没有工程的程序.利用工程可以把全部工作组合到一起,从而创建出可以执行的应用程序. 一般来说,每个工程都需要管理.也许要增加一个新的单元到 ...

  5. 《精力管理》读书笔记

    该书作者是吉姆·洛尔和托尼·施瓦茨,感谢他们让我看到如此精彩的书. 我喜欢观察自己,但是总是觉得雾里看花,看不透.生活.工作.年龄等无形的压力也让我觉得颇为焦虑和难受,但是又无力施为.我以为是我自己的 ...

  6. 《精力管理》读书笔记——第一部分

    第一部分 如何做到全情投入 第一章 什么是精力及如何管理精力 1 . 大多数人仅仅希望竭尽所能,而当生活的要求超出能力所及,我们会着眼现状提出权宜之计,或许在短期内会运作良好,而负面效应却往往在长期过 ...

  7. 《精力管理》读书笔记——第二部分

    第三部分 精力管理训练系统 建立仪式习惯的三个步骤 明确目标->正视现实->付诸行动 第八章 明确目标--知道什么最重要才能全情投入 只有树立目标,真正深刻地关心自己所做的事情,认为自己所 ...

  8. 【操作系统】《计算机的心智:操作系统之哲学管理》读书笔记

    本书从生活哲学的视角对操作系统进行了独到的阐述.从人类自然的行为规范推演到操作系统的设计与实现,以一条逻辑轴线将整个操作系统的各种原理精彩呈现到读者眼前. 这本书的语言风格比较独特,作者以风趣形象的语 ...

  9. 《计算机的心智:操作系统之哲学管理》读书笔记——原文摘录

    说明: 这本书的语言风格比较独特,作者以风趣形象的语言讲解计算机系统的原理,在读的过程中有一些比较重要的地方就做了摘录. 红字为原文目录 黑字为原书中的内容,我觉得重要,摘录出来,以备以后复习查看.若 ...

最新文章

  1. Ubuntu 系统使用命令打开某个目录下的代码文件
  2. curl获取网页内容出现乱码或为空的解决方案,另附curl_getinfo函数解析
  3. mongodb @aggregation 返回字段映射不上_MongoDB---基于分布式文件存储的数据库(二)...
  4. java正则匹配英文句号_「正则表达式」王国奇遇记
  5. mac mysql 设置短命令_短小强大的8个命令,分分钟帮你提高工作效率!
  6. mysql数据库代码_【代码总结】MYSQL数据库的常见操作
  7. 恭喜了!5 月逼自己学下这项技能,年薪 35 万起
  8. Scrapy学习之第一个简单爬取小程序
  9. 基于PHPUnit编写第一个测试用例
  10. Could not create a validated object, cause: ValidateObject failed
  11. 林斌宣布好消息!语音识别大佬、Kaldi之父加盟小米
  12. Python安装Pandas库
  13. css3 文字高光划过,CSS3实现一束光划过图片、和文字特效
  14. wordpress显示文章浏览量并且动态更新浏览量
  15. 大数据下,淘宝达人、今日特卖等自媒体人如何适应这个时代,或许该用上工具了!
  16. PS绘画效果滤镜Snap Art 4
  17. QT操作Word汇总
  18. 微信小程序/校园社区论坛/微信云开发/云函数
  19. 【推文】人间清醒VS梅花痣俏郎君
  20. TIM-定时器——STM32

热门文章

  1. Spring Bean 依赖注入
  2. 通过淘宝接口用ip获取位置信息
  3. samtools用法详解
  4. SGX_SQLite【源码分析】
  5. 野火无刷电机驱动板pcb,原理图,电源电压检测
  6. Python解题:Dream to EC-Final 梦回长安
  7. 无人机资料 固定翼 航模电机、桨叶、电池、参数与相互关系
  8. 【QT开发笔记-基础篇】| 第二章 常用控件 | 2.11 列表框 QListWidget
  9. 辛与欣(2008.12.31)
  10. 去中心化时代的创作者经济