一.为什么使用 make

Linux 程序员必须学会使用GNU make 来构建和管理自己的软件工程。GNU的make能够使整个软件工程的编译、链接只需要一个命令就可以完成。

make工具可以解决重新编译所受改动影响的源文件带来的问题。 

二.makefile

Make在执行时,需要一个命名为Makefile的文件。Makefile文件描述了整个工程的编译,连接等规则。

其中包括:1.工程中的哪些源文件需要编译以及如何编译;

2需要创建哪些库文件以及如何创建这些库文件、

3如何最后产生我们想要的可执行文件。

三.makefile的语法

文件名默认应该为:

GNUmakefile      makefile        或者Makefile然后执行make命令进行自动执行文件,并定义了源文件的依赖关系(当同一个目录下同时有这三个文件,则按照GNUmakefile     makefile         Makefile顺序优先执行)

 

Makefile(术语)

规则:用于说明如何生成一个或者多个目标文件,规则格式如下:

目标:依赖文件

(以tab空格)命令

例如:

Main.o :main.c

       gcc –c main.c     (命令是的到目标的命令)

 

目标:

Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其他的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。

       一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标被确立为最终目标。

文件名:

Make命令默认在当前目录下寻找名字为makefile或者Makefile的工程文件,当名字不为这两者之一时,可以使用如下方法指定

                                          make  -f 文件名

伪目标:

Makefile 中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phony targets.

.PHONY : clean

clean :

       rm  -f  hello  main.o  fun1.o   fun2.o

 

.PHONY”将“clean”目标声称为伪目标

 

变量:

makefile 中,存在系统默认的自动变量

(1)  $^ :代表所有的依赖文件

(2)   $@ :代表目标

(3)  $< :代表第一个依赖文件

 例:

hello : main.o  fun1.o  fun2.o

       gcc  main.o  fun1.o  fun2.o  -o  hello

 

等效于:hello : main.o  fun1.o  fun2.o

       gcc  $^  -o   $@

 

杂项:

总结:要注意的几点:

1.每一个命令行必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行。make按照命令完成相应工作

2.Makefile中“#”字符后的内容被视作注释。

3.Hello :hello.c

       @gcc  hello.c  -o  hello

@:取消回显  (不显示执行的命令:gcc  hello.c  -o  hello)

 

 四.makefile 文件中的宏

makefile文件中的宏常被用于设置编译器的选项。

在软件的开发阶段:我们一般不对编译结果进行优化,而是将调试信息包含进去。

在软件的发行版:往往跟开发阶段相反,即编译结果为不包含调试信息的容量较小的二进制可执行文件,使其执行速度尽可能的快。

makefile 文件的另一个问题是,假设编译器的名字为gcc,而其他系统的UNIX系统的编译器的名字为cc 或者c89.如果想将makefile文件移植到另一版本的UNIX系统中,或在现有系统中使用另一个编译器,为了使其工作,我们不得不修改makefile文件中许多行内容。而宏用来收集所有这些与系统相关内容的好方法,通过使用宏定义,可以方便地修改这些内容。

宏通常都是在makefile文件内部定义的,但也可以在调用make命令时在命令行上给出宏定义,例如:make  CC=c89 命令行上的宏定义将替换在makefile文件中的宏定义。当在makefile文件之外使用宏定义时,要注意宏定义必须以单个参数的形式传递,要避免在宏定义中使用空格或引号,下例是错误的:make  "CC=c89"

实验:带宏定义的makfile文件

all: myapp

# Which compiler
CC = gcc

# Where are include files kept
INCLUDE = .

# Options for development
CFLAGS = -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

我们删除旧的安装文件及中间文件*.o文件,并通过新的makefile文件创建了新的安装文件,我们看到如下输出:

#  rm  *.o   myapp

#  make  -f    makefile2

gcc  -I .  -g  -Wall  -ansi  -c  main.c

gcc  -I .  -g  -Wall  -ansi  -c   2.c
gcc  -I .  -g  -Wall  -ansi  -c   3.c

gcc  -o  myapp  main.o  2.o   3.o

实验解析:

      make 命令将$(CC)、$(CFLAGS)和$(INCLUDE)替换为相应的定义,这与C语言编译器对#define语句的处理方式一样。现在,想改变编译器命令,我们只需修改makefile文件找那个的一行即可。CC=XXX

     make内置了一些特殊的宏定义,是makefile文件更加简洁。下面是几个常见的宏,这些宏在使用前才展开

说明

$?

$@

$<

$*

 

 

列出当前目标所依赖的文件列表中比当前目标文件还要新的文件

当前目标的名字

当前依赖文件的名字

不包括后缀名的当前所依赖的文件名字

 

 

五.多个目标

通常我们所需的目标文件不只一个,或者我们希望将多个命令集中到一个位置来执行,通过扩展makfile文件即可达到这一目的。下面我们在makfile文件中增加clean选项来删除不需要的目标文件,增加install选项来编译成功的应用程序转移到另一个目录下。

实验:多个目标 

all: myapp

# Which compiler
CC = gcc

# Where to install
INSTDIR = /usr/local/bin

# Where are include files kept
INCLUDE = .

# Options for development
CFLAGS = -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

     这个makefile文件中有几处需要注意。首先,特殊目标all仍然只定义了myapp这一目标。因此,如果在执行make命令时未指定目标,它的默认行为就是创建目标myapp.

     另外一个值得关注的地方是两个新增加的目标:clean和install .目标clean用rm命令来删除目标文件。rm命令以减号-开头,减号的含义是让make命令忽略rm命令执行的结果,这意味着即使由于目标文件不存在而导致rm命令返回错误,执行命令make clean 也会成功。

       目标install依赖于myapp,所以make命令知道它必须首先创建myapp,然后才能执行制作该目标所需要的其他命令。用于制作install目标的规则有几个shell脚本命令组成。由于make命令在执行规则时会调用一个shell,并且会针对每个命令使用一个新的shell,所以我们必须在上面每行代码的结尾加上一个反斜杠\,让所有这些shell脚本命令在逻辑上处于同一行,并作为一个整体值传递给一个shell执行。这个命令以符号@开头,表示make在执行这些规则之前不会在标准输出上显示命令本身。

        目标install顺序执行多个命令以应用程序安装到其最终位置。它并不在执行下一个命令前检查前一命令的执行是否成功。如果这点很重要,我们可以将这些命令用符号&&连接起来,如下所示:

@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

&&就是“与”的意思,即每个后续命令只在前面的命令都执行成功的前提下才会被执行。
你可能不能以普通用户的身份将新命令安装到目录/usr/local/bin下,在执行命令make  install之前,可以修改makefile文件以选择另一安装目录,或是改变该目录的权限,或是通过命令su切换用户身份到超级用户root

# rm  *.o   myapp

# make  -f  makefile

gcc  -I  -g  - Wall  -ansi  -c  main.c

gcc  -I  -g  - Wall  -ansi  -c   2.c

gcc  -I  -g  - Wall  -ansi  -c   3.c

gcc  -o  myapp  main.o  2.o   3.o

# make  -f   makefile

make: nothing  to be  done  for  ‘all’

# rm  myapp

#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

实验解析:

首先,删除myapp和所有其他目标文件。单独执行make命令将使用默认目标all,并创建可执行程序mapp.然后再次运行make命令,但因为myapp已经是最新的,所以make命令未作任何事。接下来,删除myapp并执行命令make  install, 它重新创建二进制文件myapp并将其拷贝到安装目录中。最后,运行命令make clean 来删除目录下所以目标文件。

 

 

 

 

 

Logo

更多推荐