Linux/Unix环境下的make命令详解

  无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员来说简直就是一场灾难。而make工具则可自动完成编译工作,并且可以只对程序员在上次编译后修改过的部分进行编译。因此,有效的利用make和makefile工具可以大大提高项目开发的效率。同时掌握make和makefile之后,您也不会再面对着Linux下的应用软件手足无措了。

  但令人遗憾的是,在许多讲述Linux应用的书籍上都没有详细介绍这个功能强大但又非常复杂的编译工具。在这里我就向大家详细介绍一下make及其描述文件makefile。

  Makefile文件

  Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile 文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。

  在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件:

  $ make -f Makefile.debug

  例如,一个名为prog的程序由三个C源文件filea.c、fileb.c和filec.c以及库文件LS编译生成,这三个文件还分别包含自己的头文件a.h 、b.h和c.h。通常情况下,C编译器将会输出三个目标文件filea.o、fileb.o和filec.o。假设filea.c和fileb.c都要声明用到一个名为defs的文件,但filec.c不用。即在filea.c和fileb.c里都有这样的声明:

  #include "defs"

  那么下面的文档就描述了这些文件之间的相互联系:

   #It is a example for describing makefile
   prog : filea.o fileb.o filec.o
   cc filea.o fileb.o filec.o -LS -o prog
   filea.o : filea.c a.h defs
   cc -c filea.c
   fileb.o : fileb.c b.h defs
   cc -c fileb.c
   filec.o : filec.c c.h
   cc -c filec.c

  这个描述文档就是一个简单的makefile文件。

  从上面的例子注意到,第一个字符为 # 的行为注释行。第一个非注释行指定prog由三个目标文件filea.o、fileb.o和filec.o链接生成。第三行描述了如何从prog所依赖的文件建立可执行文件。接下来的4、6、8行分别指定三个目标文件,以及它们所依赖的.c和.h文件以及defs文件。而5、7、9行则指定了如何从目标所依赖的文件建立目标。

  当filea.c或a.h文件在编译之后又被修改,则 make 工具可自动重新编译filea.o,如果在前后两次编译之间,filea.C 和a.h 均没有被修改,而且 test.o 还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make 工具可避免许多不必要的编译工作。当然,利用 Shell 脚本也可以达到自动编译的效果,但是,Shell 脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而 make 工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。

  Makefile文件作为一种描述文档一般需要包含以下内容:

  ◆ 宏定义
  ◆ 源文件之间的相互依赖关系
  ◆ 可执行的命令

  Makefile中允许使用简单的宏指代源文件及其相关编译信息,在Linux中也称宏为变量。在引用宏时只需在变量前加$符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。

  下面都是有效的宏引用:

  $(CFLAGS)
  $2
  $Z
  $(Z)

  其中最后两个引用是完全一致的。

  需要注意的是一些宏的预定义变量,在Unix系统中,$*、$@、$?和$<四个特殊宏的值在执行命令的过程中会发生相应的变化,而在GNU make中则定义了更多的预定义变量。关于预定义变量的详细内容,宏定义的使用可以使我们脱离那些冗长乏味的编译选项,为编写makefile文件带来很大的方便。

   # Define a macro for the object files
   OBJECTS= filea.o fileb.o filec.o
   # Define a macro for the library file
   LIBES= -LS
   # use macros rewrite makefile
   prog: $(OBJECTS)
   cc $(OBJECTS) $(LIBES) -o prog
   ……

  此时如果执行不带参数的make命令,将连接三个目标文件和库文件LS;但是如果在make命令后带有新的宏定义:

  make "LIBES= -LL -LS"

  则命令行后面的宏定义将覆盖makefile文件中的宏定义。若LL也是库文件,此时make命令将连接三个目标文件以及两个库文件LS和LL。

  在Unix系统中没有对常量NULL作出明确的定义,因此我们要定义NULL字符串时要使用下述宏定义:

  STRINGNAME=

  Make命令

  在make命令后不仅可以出现宏定义,还可以跟其他命令行参数,这些参数指定了需要编译的目标文件。其标准形式为:

  target1 [target2 …]:[:][dependent1 …][;commands][#…]
  [(tab) commands][#…]

  方括号中间的部分表示可选项。Targets和dependents当中可以包含字符、数字、句点和"/"符号。除了引用,commands中不能含有"#",也不允许换行。

  在通常的情况下命令行参数中只含有一个":",此时command序列通常和makefile文件中某些定义文件间依赖关系的描述行有关。如果与目标相关连的那些描述行指定了相关的command序列,那么就执行这些相关的command命令,即使在分号和(tab)后面的aommand字段甚至有可能是NULL。如果那些与目标相关连的行没有指定command,那么将调用系统默认的目标文件生成规则。

  如果命令行参数中含有两个冒号"::",则此时的command序列也许会和makefile中所有描述文件依赖关系的行有关。此时将执行那些与目标相关连的描述行所指向的相关命令。同时还将执行build-in规则。

  如果在执行command命令时返回了一个非"0"的出错信号,例如makefile文件中出现了错误的目标文件名或者出现了以连字符打头的命令字符串,make操作一般会就此终止,但如果make后带有"-i"参数,则make将忽略此类出错信号。

  Make命本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。其标准形式为:

  Make [flags] [macro definitions] [targets]

  Unix系统下标志位flags选项及其含义为:

  -f file  指定file文件为描述文件,如果file参数为"-"符,那么描述文件指向标准输入。如果没有"-f"参数,则系统将默认当前目录下名为makefile或者名为Makefile的文件为描述文件。在Linux中, GNU make 工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索 makefile文件。

  -i   忽略命令执行返回的出错信息。
  -s   沉默模式,在执行之前不输出相应的命令行信息。
  -r   禁止使用build-in规则。
  -n   非执行模式,输出所有执行命令,但并不执行。
  -t   更新目标文件。
  -q   make操作将根据目标文件是否已经更新返回"0"或非"0"的状态信息。
  -p   输出所有宏定义和目标文件描述。
  -d   Debug模式,输出有关文件和检测时间的详细信息。

  Linux下make标志位的常用选项与Unix系统中稍有不同,下面我们只列出了不同部分:

  -c dir   在读取 makefile 之前改变到指定的目录dir。
  -I dir   当包含其他 makefile文件时,利用该选项指定搜索目录。
  -h   help文挡,显示所有的make选项。
  -w   在处理 makefile 之前和之后,都显示工作目录。

  通过命令行参数中的target ,可指定make要编译的目标,并且允许同时定义编译多个目标,操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。

  通常,makefile 中还定义有 clean 目标,可用来清除编译过程中的中间文件,例如:

  clean:
  rm -f *.o

  运行 make clean 时,将执行 rm -f *.o 命令,最终删除所有编译过程中产生的所有中间文件。

  隐含规则

  在make 工具中包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件建立特定类型的目标。Unix系统通常支持一种基于文件扩展名即文件名后缀的隐含规则。这种后缀规则定义了如何将一个具有特定文件名后缀的文件(例如.c文件),转换成为具有另一种文件名后缀的文件(例如.o文件):

  .c:.o
  $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
  系统中默认的常用文件扩展名及其含义为:
  .o  目标文件
  .c  C源文件
  .f  FORTRAN源文件
  .s  汇编源文件
  .y  Yacc-C源语法
  .l  Lex源语法

  在早期的Unix系统系统中还支持Yacc-C源语法和Lex源语法。在编译过程中,系统会首先在makefile文件中寻找与目标文件相关的.C文件,如果还有与之相依赖的.y和.l文件,则首先将其转换为.c文件后再编译生成相应的.o文件;如果没有与目标相关的.c文件而只有相关的.y文件,则系统将直接编译.y文件。

  而GNU make 除了支持后缀规则外还支持另一种类型的隐含规则--模式规则。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。模式规则看起来非常类似于正则规则,但在目标名称的前面多了一个 % 号,同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个 file.c 文件转换为 file.o 文件:

  %.c:%.o
  $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
#EXAMPLE#

  下面将给出一个较为全面的示例来对makefile文件和make命令的执行进行进一步的说明,其中make命令不仅涉及到了C源文件还包括了Yacc语法。本例选自"Unix Programmer's Manual 7th Edition, Volume 2A" Page 283-284

  下面是描述文件的具体内容:

   #Description file for the Make command
   #Send to print
   P=und -3 | opr -r2
   #The source files that are needed by object files
   FILES= Makefile version.c defs main.c donamc.c misc.c file.c \
   dosys.c gram.y lex.c gcos.c
   #The definitions of object files
   OBJECTS= vesion.o main.o donamc.o misc.o file.o dosys.o gram.o
   LIBES= -LS
   LINT= lnit -p
   CFLAGS= -O
   make: $(OBJECTS)
   cc $(CFLAGS) $(OBJECTS) $(LIBES) -o make
   size make
   $(OBJECTS): defs
   gram.o: lex.c
   cleanup:
   -rm *.o gram.c
   install:
   @size make /usr/bin/make
   cp make /usr/bin/make ; rm make
   #print recently changed files
   print: $(FILES)
   pr $? | $P
   touch print
   test:
   make -dp | grep -v TIME>1zap
   /usr/bin/make -dp | grep -v TIME>2zap
   diff 1zap 2zap
   rm 1zap 2zap
   lint: dosys.c donamc.c file.c main.c misc.c version.c gram.c
   $(LINT) dosys.c donamc.c file.c main.c misc.c version.c \
   gram.c
   rm gram.c
   arch:
   ar uv /sys/source/s2/make.a $(FILES)

  通常在描述文件中应象上面一样定义要求输出将要执行的命令。在执行了make命令之后,输出结果为:

  $ make
  cc -c version.c
  cc -c main.c
  cc -c donamc.c
  cc -c misc.c
  cc -c file.c
  cc -c dosys.c
  yacc gram.y
  mv y.tab.c gram.c
  cc -c gram.c
  cc version.o main.o donamc.o misc.o file.o dosys.o gram.o \
  -LS -o make
  13188+3348+3044=19580b=046174b

  最后的数字信息是执行"@size make"命令的输出结果。之所以只有输出结果而没有相应的命令行,是因为"@size make"命令以"@"起始,这个符号禁止打印输出它所在的命令行。

  描述文件中的最后几条命令行在维护编译信息方面非常有用。其中"print"命令行的作用是打印输出在执行过上次"make print"命令后所有改动过的文件名称。系统使用一个名为print的0字节文件来确定执行print命令的具体时间,而宏$?则指向那些在print文件改动过之后进行修改的文件的文件名。如果想要指定执行print命令后,将输出结果送入某个指定的文件,那么就可修改P的宏定义:

  make print "P= cat>zap"

  在Linux中大多数软件提供的是源代码,而不是现成的可执行文件,这就要求用户根据自己系统的实际情况和自身的需要来配置、编译源程序后,软件才能使用。只有掌握了make工具,才能让我们真正享受到到Linux这个自由软件世界的带给我们无穷乐趣。

(0)

相关推荐

  • 详解linux下make命令的使用方法

    本文我们将用一些实例来讨论 make 命令背后的工作机制. Make 如何工作的 对于不知道背后机理的人来说,make 命令像命令行参数一样接收目标.这些目标通常存放在以 "Makefile" 来命名的特殊文件中,同时文件也包含与目标相对应的操作.更多信息,阅读关于 Makefiles 如何工作的系列文章. 当 make 命令第一次执行时,它扫描 Makefile 找到目标以及其依赖.如果这些依赖自身也是目标,继续为这些依赖扫描 Makefile 建立其依赖关系,然后编译它们.一旦主依

  • linux下make命令实现输出高亮的方法

    前言 无论是在linux 还是在Unix环境 中,make都是一个非常重要的编译命令.最近在调试代码,make 输出信息的时候 error .warning 等信息都是和普通信息一样白色打出来的,和普通信息一样很难区分.每次就要在茫茫多的输出信息中找那个小小的error 才看是哪一行出了问题,有时候运气不好还可能来来回回看好几遍才找到. 这里提供一种解决办法进行相关处理,让make输出的信息不再那么难找. 注意:gcc 在4.9 版本后已经有相关的颜色标记了,直接添加 -fdiagnostics

  • 实例详解Linux下的Make命令

    前言 无论是在linux 还是在Unix环境 中,make都是一个非常重要的编译命令.不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install.利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和 makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系.而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员 来说简直就是一场灾难.而make工具则可自动完成编译

  • Linux/Unix环境下的make命令详解

    无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令.不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install.利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系.而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员来说简直就是一场灾难.而make工具则可自动完成编译工作,并且可以

  • Linux/Unix环境下的Make和Makefile详解

    Linux/Unix环境下的Make和Makefile详解 无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令.不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install.利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系.而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员来说简

  • linux 下的yum命令详解

    yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载.安装.yum提供了查找.安装.删除某一个.一组甚至全部软件包的命令,而且命令简洁而又好记. yum的命令形式一般是如下:yum [options] [command] [package ...] 其中的[opt

  • 内网环境下registry搭建步骤详解

    目录 背景 docker环境 配置http可访问 重新加载docker 启动registry 启动registry-web 背景 在实际的开发运维过程中,经常需要使用镜像仓库的情况,虽然阿里云.腾讯云等都提供了带有免费额度的镜像仓库服务,但是由于账号等问题,我们还是需要自己搭建一个镜像仓库服务,供内网环境下使用. 大致的步骤: 准备docker环境 配置非http访问( Insecure Registries) 重新家在docker 启动registry 启动registry-web docke

  • 虚拟机Linux桥接模式下设置静态IP详解

    本文研究的主要是虚拟机Linux桥接模式下设置静态IP的相关内容,具体介绍如下. 之前一直使用NAT模式,测试时android端远程访问虚拟机的mysql时发现无法连接,但是访问同学拷过来的虚拟机Linux的mysql却成功了,想了下原因是他设置的桥接模式.关于两种模式的区别,网上可以搜到一大堆文章,通俗点讲,NAT模式下,虚拟机从属于主机,也就是访问外部网络必须通过主机来访问,因此虚拟机的IP只有主机才能识别.而桥接模式下,虚拟机和主机是平行关系,共享一张网卡(使用网卡的多个接口),可以直接访

  • 一天一个shell命令 linux文本操作系列-head,tail命令详解

    head命令中文介绍: 用法:head [选项]... [文件]... 将每个指定文件的头10 行显示到标准输出. 如果指定了多于一个文件,在每一段输出前会给出文件名作为文件头. 如果不指定文件,或者文件为"-",则从标准输入读取数据. 长选项必须使用的参数对于短选项时也是必需使用的. -c, --bytes=[-]K 显示每个文件的前K 字节内容: 如果附加"-"参数,则除了每个文件的最后K字节数据外 显示剩余全部内容 -n, --lines=[-]K 显示每个文

  • Linux系统多网卡环境下的路由配置详解

    Linux下路由配置命令 1. 添加主机路由 route add -host 192.168.1.11 dev eth0 route add -host 192.168.1.12 gw 192.168.1.1 2. 添加网络路由 route add -net 192.168.1.11 netmask 255.255.255.0 eth0 route add -net 192.168.1.11 netmask 255.255.255.0 gw 192.168.1.1 route add -net

  • Linux下的chkconfig命令详解

    chkconfig命令 Linux下的chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. 使用语法 chkconfig [–add][–del][–list][系统服务] 或 chkconfig [–level <等级代号>][系统服务][on/off/reset] chkconfig在没有参数运行时,显示用法.如果加上服务名,那么就检查这个服务是否在当前运行级启动.如果是,返回true

  • 一天一个shell命令 linux文本内容操作系列-sed命令详解

    说明: sed是stream editor(流编辑器)的缩写.它能够完美匹配正则表达式.sed和awk是文件编辑最重要的两个命令了.尤其涉及到了很多正则表达式的问题,笔者不敢也有点犯怵,试着写写. 实例: 1.替换文件中的字符串 $sed -i 's/text/replace/g' file #如果不加g结尾,则替换每一行的第一个 #如果只是打印,去掉-i 2.忽略前N处匹配,从N+1出开始替换 $sed -i 's/text/replace/2g' file #在g前面加入数字N 3.移除空白

  • windows环境下tensorflow安装过程详解

    一.前言 本次安装tensorflow是基于Python的,安装Python的过程不做说明(既然决定按,Python肯定要先了解啊):本次教程是windows下Anaconda安装Tensorflow的过程(cpu版,显卡不支持gpu版的...) 二.安装环境 (tensorflow支持的系统是64位的,windows和linux,mac都需要64位) windows7(其实和windows版本没什么关系,我的是windows7,安装时参照的有windows10的讲解) Python3.5.2(

随机推荐