首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

Linux中的make跟makefile

2013-10-08 
Linux中的make和makefile1.1多源文件问题我们平常编写一些小的程序一般都是修改源代码后,重新编译所有的源

Linux中的make和makefile
1.1      多源文件问题

我们平常编写一些小的程序一般都是修改源代码后,重新编译所有的源文件,但是对于大型的项目,这种方法就会有许多的问题。edit?compile?test周期耗费的时间比较长,所以当只修改一个文件时,要避免编译所有的源文件。

     假设有头文件a.h, b.h , c.h和C的源文件main.c, 2.c , 3.c。它们的关系如下:

/* main.c */#include "a.h".../* 2.c */#include "a.h"#include "b.h".../* 3.c */#include "b.h"#include "c.h"

 

如果c.h修改了,源文件main.c和2.c应该不需要重新编译,因为它们不依赖c.h头文件。而源文件3.c依赖头文件c.h需要重新编译。如果b.h修改了,我们忘记重新编译2.c也会导致程序功能异常。 而make工具可以解决上面这两个问题。

1.2      make命令和makefiles

   make命令有一些内置的默认功能,但是光有这个它自己还是不知道怎么build程序。我们必须提供一个文件告诉make应用程序的构造,这个文件就是makefile.

Make和makefile提供了强大的功能来管理项目的编译以及发布install到指定文件夹。

1.2.1 makefiles语法

一个makefile文件包括一系列的依赖(dependency))关系和规则(rules)。一个依赖有一个目标文件和一系列目标文件依赖的源文件。规则描述了怎么用依赖文件创建目标文件。通常,一个目标文件是单个可执行文件。Make命令通过读取makefile来决定哪些源文件和目标文件需要重新编译,根据比较它们的日期和时间来决定使用来个规则来构建targets。

 

1.2.2 make的选项与参数

下面为make 命令经常用到的三个选项:

   -k:告诉make当发生错误时继续执行,而不是停在第一个错误的位置。

   -n:告诉make打印出它将要完成什么,实际上没做。

   ?f <filename>,告诉make用哪个makefile,如果不使用这个选项,make将搜索当前目录下第一个名为makefile的文件,如果还不存在,搜索一个文件名为Makefile。

 

 依赖:指明了每个文件怎么样关联源文件。

在makefile文件中,写依赖的方法为:目标文件名,冒号,空格或tabs,然后接着是用空格或 tab分开的文件列表,用于创建目标文件。

 依赖的例子:

myapp: main.o 2.o 3.o

main.o: main.c a.h

2.o: 2.c a.h b.h

3.o: 3.c b.h c.h

解释:myapp依赖 main.o, 2.o和 3.o, main.o依赖 main.c和 a.h,以此类推。

从上面可以很清楚的看到,当b.h改变了,我们需要修改2.o和3.o,当2.o和3.o改变了,需要重新编译myapp.

 当需要编译多个文件,可以使用目标文件名all.假设应用程序包含一个二进制文件myapp和一个用户指导页面myapp.1,可以使用下面的方式指定。

all:  myapp  myapp.1

规则:是makefile的第二部分内容。告诉我们将如何创建目标文件。

当make命令决定2.o需要重新编译,使用什么命令进行编译呢?也许最简单的方式是使用gcc –c 2.c(make命令也有许多默认的规则),但是如果需要包含头文件的目录以及为调试设置符号信息怎么办呢?可以在makefile当中使用显示的规则。

 

 重要:所有的规则都必须要以制表符(tab)开头的行,空格不行。由于多个空格和tab看起来的效果很相同,但在Unix编程当中,tabs和spaces还是有区别的,会导致一些问题。在makefile文件末尾加入空格行会导致make命令不能执行,因为缺少tab。

例子:一个简单的 makefile,命名为Makefile1

myapp: main.o 2.o 3.ogcc ?o myapp main.o 2.o 3.o  //该行必须要有一个tab开头main.o: main.c a.hgcc ?c main.c2.o: 2.c a.h b.hgcc ?c 2.c3.o: 3.c b.h c.hgcc ?c 3.c


 

使用$ make ?f Makefile1

Linux中的make跟makefile

这是由于make命令分析到目标文件为myapp,然后查看依赖文件main.o,需要main.c来创建,但是开始没有创建该文件。

 使用下面的命令创建三个空头文件:

$touch a.h

$touch b.h

$touch c.h

 

/* main.c */#include "a.h"extern void function_two();extern void function_three();int main(){function_two();function_three();exit (EXIT_SUCCESS);}/* 2.c */#include "a.h"#include "b.h"void function_two() {}/* 3.c */#include "b.h"#include "c.h"void function_three() {}


$ make ?f Makefile1

Make按顺序显示出了它执行的命令

Linux中的make跟makefile

现在修改b.h头文件:

$ touch b.h

$ make ?f Makefile1

Make只显示了创建依赖于b.h的目标 文件的命令

Linux中的make跟makefile

删除2.o

$ rm 2.o

$ make ?f Makefile1

Linux中的make跟makefile

由上面可以看到make 可以正确的决定执行gcc命令的顺序。

在makefile中使用#进行注释。

1.2.3 makefiles宏

对于文件比较多的项目,可以在makefile中定义宏。

定义:MACRONAME=value

获取值:$(MACRONAME) or ${MACRONAME}.

宏经常在makefile中用来为编译器指定编译选项。对于发布版本和调试版本程序需要的信息不同,对于release 版本不需要加入调试信息。对于Makefile1,默认使用的是gcc编译器,在其他的Unix操作系统,可能使用cc或cc89,如果想让makefile在不同的操作系统上运行,需要修改makefile文件中的多行。可以使用宏来解决这问题。

Makefile2:

all: myapp # 指定编译器CC = gcc # 头文件目录INCLUDE = . # 开发编译选项CFLAGS = -g -Wall -ansi # 发布版本编译选项# CFLAGS = -O -Wall -ansi  myapp: main.o 2.o 3.o         $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h         $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h         $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h         $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c


 

执行下面命令:

$ rm *.o myapp

$ make ?f Makefile2

 Linux中的make跟makefile

实际上,make有许多特殊的内部宏,可以让makefile更简洁。下面列出一些经常用到的内部宏。

 

$?

List of prerequisites changed more recently than the current target.

$@

Name of the current target.

$<

Name of the current prerequisite.

$*

Name of the current prerequisite, without any suffix.

 

Makefile中另外两个非常重要的特殊字符:

“-“让make忽略任何错误。

“@”让make在命令执行前不要向标准输出打印命令。

1.2.4 多目标文件

  可以使用”clean”选项来清除不想要的目标文件,用”install”选项来将应用程序移动到不同的目录中。Makefile3

all: myapp # Which compilerCC = gcc # Where to installINSTDIR = /usr/local/bin # Where are include files keptINCLUDE = . # Options for developmentCFLAGS = -g -Wall -ansi # Options for release# CFLAGS = -O -Wall -ansi  myapp: main.o 2.o 3.o       $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h       $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h       $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h       $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c  clean:       -rm main.o 2.o 3.o install: myapp       @if [ -d $(INSTDIR) ]; \       then \              cp myapp $(INSTDIR);\              chmod a+x $(INSTDIR)/myapp;\              chmod og-w $(INSTDIR)/myapp;\              echo "Installed in $(INSTDIR)";\       else \              echo "Sorry, $(INSTDIR) does not exist";\       fi


 

install依赖于myapp.,make需要先生成myapp。

If前面的“@”是让make不要输出这些命令。

$ make ?f Makefile3 install

gcc ?o myapp main.o 2.o 3.o

Installed in /usr/local/bin

$ make ?f Makefile3 clean

rm main.o 2.o 3.o

1.2.5 内置规则

Makefile能准确判断每个命令的执行步骤,make中包括许多内置的规则,当有许多源文件时,可以简化makefiles。创建一个foo.c文件:

#include <stdlib.h>#include <stdio.h>int main(){printf("Hello World\n");exit (EXIT_SUCCESS);}


 

使用make进行编译,不指定makefile.

$ make foo

Make命令知道怎么调用编译器,尽管使用的是默认的编译器cc.

可以使用 make –p来打印内置的规则。

Linux中的make跟makefile

我们可以用自定义的值来改变内置宏,从而改变它的默认行为。

$ rm foo

$ make CC = gcc CFLAGS = "?Wall ?g" foo

 

1.2.6 后缀规则

 有时源文件需要在不同的编译器上进行编译:如在MS-DOS或者是Linux的GCC下。在MS-DOS下的源文件后缀名大多为.cpp而不是C,而Linux下的make命令没有内置的规则来编译.cpp文件(有.cc的编译规则)。

我们需要让make产生一个新规则来生成 .cpp的目标文件,这时需要添加一个后缀规则。Makefile5:

all: myapp .SUFFIXES:      .cpp .cpp.o:         $(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $< # Which compilerCC = gcc # Where to installINSTDIR = /usr/local/bin # Where are include files keptINCLUDE = . # Options for developmentCFLAGS = -g -Wall -ansi # Options for release# CFLAGS = -O -Wall -ansi  myapp: main.o 2.o 3.o         $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h         $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h         $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h         $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c clean:         -rm main.o 2.o 3.o install: myapp         @if [ -d $(INSTDIR) ]; \                   then \                   cp myapp $(INSTDIR);\                   chmod a+x $(INSTDIR)/myapp;\                   chmod og-w $(INSTDIR)/myapp;\                   echo "Installed in $(INSTDIR)";\         else \                   echo "Sorry, $(INSTDIR) does not exist";\         fi


 

执行:

$ cp foo.c bar.cpp

$ make ?f Makefile5 bar

gcc ?xc++ ?g ?Wall ?ansi ?I. ?c bar.cpp

 

?xc++:告诉gcc是一个C++源文件

1.2.7 make管理库文件lib

  当开发一个大的项目,利用一个库(library)来管理多个编译版本产品非常方便。库文件的后缀名为(.a),它包含许多的目标文件。

Make命令中内置的规则来管理库:

.c.a:

$(CC) ?c $(CFLAGS) $<

$(AR) $(ARFLAGS) $@ $*.o

宏 $(AR) 和 $(ARFLAGS) 指的是命令 ar 和命令选项 rv。含义为:从一个 .c 文件到一个.a的库文件需要应用两条规则。第一条规则:必须编译源文件为目标文件;第二条规则:使用ar命令来添加该新的目标文件来生成库文件。如果目前有一个库文件fud包含目标文件bas.o,在第一条规则中,$<为bas.c。在第二条规则中,$@为fud.a,$*为bas.

 

下面让目标文件2.o 和3.o包含在库文件 mylib.a,如下Makefile6:

all: myapp # Which compilerCC = gcc # Where to installINSTDIR = /usr/local/bin # Where are include files keptINCLUDE = . # Options for developmentCFLAGS = -g -Wall -ansi # Options for release# CFLAGS = -O -Wall -ansi # Local LibrariesMYLIB = mylib.a myapp: main.o $(MYLIB)         $(CC) -o myapp main.o $(MYLIB)  $(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o)main.o: main.c a.h2.o: 2.c a.h b.h3.o: 3.c b.h c.h clean:         -rm main.o 2.o 3.o $(MYLIB) install: myapp         @if [ -d $(INSTDIR) ]; \         then \                   cp myapp $(INSTDIR);\                   chmod a+x $(INSTDIR)/myapp;\                   chmod og-w $(INSTDIR)/myapp;\                   echo "Installed in $(INSTDIR)";\         else \                   echo "Sorry, $(INSTDIR) does not exist";\         fi


 

$ rm ?f myapp *.o mylib.a

$ make ?f Makefile6

gcc ?g ?Wall ?ansi ?c main.c ?o main.o

gcc ?g ?Wall ?ansi ?c 2.c ?o 2.o

ar rv mylib.a 2.o

ar: creating mylib.a

c ? 2.o

gcc ?g ?Wall ?ansi ?c 3.c ?o 3.o

ar rv mylib.a 3.o

c ? 3.o

gcc ?o myapp main.o mylib.a

 

1.2.8 GCC的两个选项-JN  –MM

-jN可以同时执行N条命令

-MM:gcc会输出目标文件所依赖的源文件。

 

$ gcc ?MM main.c 2.c 3.c

main.o: main.c a.h

2.o: 2.c a.h b.h

3.o: 3.c b.h c.h

Make命令知道怎么调用编译器,尽管使用的是默认的编译器cc.

我们可以用自定义的值来改变内置宏,从而改变它的默认行为。

$ rm foo

$ make CC = gcc CFLAGS = "?Wall ?g" foo

 

 

热点排行