Make

###一、make的功能:

make是一个用来维护程序模块关系和生产可执行文件的工具,他可以根据程序修改的情况重新编译链接生成的中间代码或最终的可执行文件。执行make命令需要一个Makefile文件,来定义整个项目的编译规则。makefile定义了模块间的依赖关系,指定文件的编译顺序,以及编译所使用的命令。有了make和Makefile文件,整个项目的源程序可以自动编译,极大的提高了软件开发效率。

###二、Make的一般使用:

1、Makefile的基本构成:

Makefile由规则构成,一条规则生成一个或多个目标文件,其格式如下:

目标文件列表 分隔符 依赖文件列表 [;命令]  //[]中的内容可选
    [命令]
    [命令]

例如:

main:main.o module1.o module2.o
	gcc main.o module1.o module2.o -o main

第二行是命令必须以Tab键开头,第一行是说明模块间的依赖关系,所以不加Tab,若加Tab,make就认为这是一条命令。以#开头的行为注释行,makefile中若用到#,可用#;同样,$应该用$$。在依赖列表后加上分号后,可直接跟上命令。

命令之间可以插入多个空行,但每行必须有Tab键,如果一行过长,可以在行末输入一个\,用反斜杠连接的行都被看成一行来处理,反斜杠和新行之间不能有空格。Makefile也可以命名为makefile,若命名为其他文件名,则需要用-f或--file选项来告知make哪一个是makefile文件。

2、Makefile文件的构成:

一个完整的makefile文件由5个部分构成:显式规则、隐含规则、变量、文件指示和注释。

显式规则:一条显式规则指名了目标文件、目标文件的依赖关系、命令。有些规则没有命令,只是说明文件之间的依赖关系。

隐含规则:由make根据目标文件而自动推导出的规则。例如:module2.o:head1.h,实际上等价于module2.o:module2.c head1.h; gcc -c module2.c -o module2.o

变量:类似于c语言中的宏。

文件指示:包括三个部分,一个类似于c语言中的include语句,可以将另一个makefile文件包含进来;二是根据情况指定makefile中的有效部分,就像c语言中的预编译#if一样;三是定义一个多行的命令。

3、makefile的基本语法:

  • |的作用: foo:foo.c | somelib gcc -o foo foo.c somelib 当somelib文件的时间戳比foo晚时,不用重新编译foo。
  • 命令行属性:

可在命令前、Tab键后加上如下符号:

  -:执行本命令行如果遇到错误,继续执行而不退出make。
  +:总是执行该命令,即使执行make时使用了-n,-q,-t选项。
  @:执行命令时不在屏幕上输出该命令的内容。
  • 伪目标:

先看以下代码:

clean:
	-rm -rf *.o

因为clean后没有依赖文件,所以clean被认为是最新,不会执行rm命令,若要执行rm,需要执行make clean命令。但如果当前目录下已经有一个名叫clean的文件,则make clean也不能使得rm命令执行。这时需要引入伪目标才能搞定。

.PHONY:clean
clean:
	-rm -rf *.o

.PHONY的依赖文件为伪目标,作用是伪目标的命令即使在当前目录下存在与伪目标同名的文件时也执行该命令。

  • 特殊目标:

.PHONY:伪目标,如上

.IGNORE:对于该目标后的依赖文件,生成时如遇到错误则可跳过错误继续执行,不会中断make。

.SUFFIXES:该目标的依赖被认为是一个后缀列表,在检查后缀规则时使用。

.SILENT:生成该目标文件的依赖文件所执行的命令都不被打印,如果其后无依赖文件,则所有的命令都不会被打印。

.PRECIOUS:该目标的依赖文件将不会被删除。

.INTERMEDIATE:该目标的依赖文件被当作是中间文件对待。

  • 多个目标:

一个规则中可以有多个目标,这些目标有相同的依赖文件

  • 搜索目录:

通常在一个大的项目中,会把头文件、源文件、库文件放在不同的目录下。当目录发生改变后,只需改变依赖文件的搜索目录。 make定义了一个叫VPATH的变量,在当前目录中搜索不到依赖文件时,便到VPATH定义的目录中去寻找。也可用关键字vpath 定义搜索位置:

vpath <pattern> <directories>
vpath :清除所有已被设置好的文件搜索目录。

如:vpath %.h ../headers表示在../headers目录下搜索所有.h结尾的头文件。

  • 变量:

makefile中通常可定义变量,make在执行时会把变量名出现的地方用变量值代替。

引用变量:$()或者${}

定义变量:

 = :定义的变量在执行命令时才展开;
 :=:定义的变量立即展开;
 ?=:在此之前没有给该变量赋值才会给该变量赋值
 +=:追加变量值,与原变量值之间用空格隔开

预定义变量:

makefile 中预定义了许多变量,在隐含规则中通常会用到这些变量:

     宏名		初始值		说明
     CC			cc			默认使用的编译器
     CFLAGS		-o			编译器使用的选项
     MAKE		make		make命令
     MAKEFLAGS	空 			make命令的选项
     SHELL					默认使用的shell名
     PWD					运行make时的当前路径
     AR			ar			库管理命令
     ARFLAGS   	-ruv		库管理选项
     LIBSUFFIXE	.a			库的后缀
     A			a			库的扩展名

自动变量:

它们的值在make运行过程中动态的改变,是隐含规则所必需的变量。

     $@:表示一个规则中的目标文件名。
     $%:当规则中的目标文件是一个静态库文件时,$%就代表静态库的一个成员名。如果目标不是静态库文件,则该变量
         值为空。
     $<:规则中的第一个依赖文件名。
     $>:当规则是一个静态库文件时,该变量表示静态库名。
     $?:所有比目标文件新的依赖文件列表,以空格分隔。如果目标是静态库文件,该变量代表的是
         库成员。
     $^:规则的所有依赖文件列表,以空格分割。如果目标是静态库文件,代表的是库成员。
     $+:和$^类似,不同的是该变量不除去重复的文件。
     $*:去掉后缀的目标文件名。
  • 条件语句: ifeq ($(CC),gcc) libs=$(libs_for_gcc) else libs=$(normal_libs) endif foo:foo.c $(CC) -o foo foo.c $(libs)

此外,还有ifneq,ifdef,ifndef

  • 使用库:

建立和维护库时,把库名作为目标文件,需要加入库中的文件作为依赖文件。如:

  mylib.a:mylib.a(file1.o) mylib.a(file2.o)或
  mylib.a:mylib.a(file1.o file2.o)

接着输入命令:

ar -ruv 库名 目标文件名

三、make命令的常用选项:

-C dir或--directory=DIR:在读取Makefile文件之前,先切换到dir目录下,即把dir目录作为当前目录。
-d:输出所有的调试信息。
-e或--environment-overrides:不允许makefile对系统环境变量进行重新赋值。
-f filename或者--file=FILE或者--makefile=FILE:使用指定文件作为makefile文件。
-i或者--ignore-errors:忽略执行make时产生的错误,不退出make。
-h或者--help:打印出帮助信息。
-n或者--just-print:只打印出要执行的命令,但不执行命令。
-o filename或者--old-file=FILE:不重建filename及依赖于filename的文件。
-p:执行命令之前,打印出make读取的makefile的所有数据,同时打印出make的版本信息。如果只打印信息而
	不执行命令,可使用make -qp ,查看make执行前的隐含规则和预定义变量,使用make -p-f /dev/null。
-q:不执行任何命令,返回0表示没有重建目标,返回1表示存在重建目标,返回2表示有错误发生。
-r:忽略隐含规则。
-R:取消预定义变量。同时打开-r选项。
-s:执行但不显示所执行的命令。
-t:把所有目标文件的最后修改时间设置为当前系统时间。
-v:打印make版本信息。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PHP在线

拒绝重复造轮子,用composer搞自己的框架(2)

久负盛名的 CodeIgniter 框架是很多人的 PHP 开发入门框架,同样也是我开始学习如何从头构建一个网站的框架。在 CI中我学到了很多,其中对 MVC ...

38790
来自专栏coder修行路

Python 并发编程(一)之线程

常用用法 t.is_alive() Python中线程会在一个单独的系统级别线程中执行(比如一个POSIX线程或者一个Windows线程) 这些线程将由操作系统...

29160
来自专栏社区的朋友们

深入浅出 Nodejs( 二 ):Nodejs 文件模块机制

本篇教程关于Nodejs的文件模块机制,具体讲CommonJs规范以及Nodejs文件模块的实现原理。

53920
来自专栏IT技术精选文摘

Nginx模块之Filter解析

过滤模块简介 执行时间和内容 过滤(filter)模块是过滤响应头和内容的模块,可以对回复的头和内容进行处理。它的处理时间在获取回复内容之后,向用户发送响...

28390
来自专栏difcareer的技术笔记

使用AndroidStudio调试AOSP源码

当我们在AOSP中加入了自己的代码,或者当我们阅读源码时,一个迫切的需求就是调试,下面讲述如何利用AndroidStudio调试Java部分的代码。

13530
来自专栏高性能分布式系统设计

Go的PCRE包装在启用JIT的时候遇到的问题和解决方案

用CGO包装C的库, 如果启用了JIT的话,会有一些问题. 例如pcre用了JIT动态生成代码并JIT代码引用里自己线程的JIT Stack数据. 这时会有一个...

31070
来自专栏along的开发之旅

Java Web之Tomcat目录结构和Web应用

如果Servlet版本小于3.0, 或者您不希望使用注解, 那么可以在web.xml中配置您的servlet程序, 一个最简单的实例如下:

12920
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(三十四) ——Redis Lua脚本环境设计与实现

《Redis设计与实现》读书笔记(三十四) ——Redis Lua脚本环境设计与实现 (原创内容,转载请注明来源,谢谢) 一、创建lua环境 为了在redis...

41650
来自专栏Java后端技术

解决jetty7.0.pre5启动时报ClassNotFoundException: javax.interceptor.InvocationContext异常的问题

一.背景介绍:最近项目在使用maven命令行运行jetty服务器时出现ClassNotFoundException: javax.interceptor.Inv...

8010
来自专栏GreenLeaves

五、CLR加载程序集代码时,JIT编译器对性能的产生的影响

1、CLR首次加载代码造成的性能损失      四、CLR执行程序集中代码介绍了CLR在首次执行一个类的时,会初始化一个内部结构,然后当目标方法被首次调用时,...

31370

扫码关注云+社区

领取腾讯云代金券