makefile简单入门

Makefile简介

在软件开发中,make通常被视为一种软件构建工具。该工具主要经由读取一种名为“makefile”或“Makefile”的文件来实现软件的自动化建构。它会通过一种被称之为“target”概念来检查相关文件之间的依赖关系,这种依赖关系的检查系统非常简单,主要通过对比文件的修改时间来实现。在大多数情况下,我们主要用它来编译源代码,生成结果代码,然后把结果代码连接起来生成可执行文件或者库文件。
优点与缺点
与大多数古老的Unix工具一样,make也分别有着人数众多的拥护者和反对者。它在适应现代大型软件项目方面有着许许多多的问题。但是,依然有很多人坚定地认为(包括我)它能应付绝大多数常见的情况,而且使用非常的简单,功能强大,表达清楚。无论如何,make如今仍然被用来编译很多完整的操作系统,而且它的那些“更为现代”的替代品们在基本操作上与它没有太大差别。
当然,随着现代的集成开发环境(IDE)的诞生,特别是非Unix的平台上,很多程序员不再手动管理依靠关系检查,甚至不用去管哪些文件是这个项目的一部分,而是把这些任务交给了他们的开发环境去做。类似的,很多现代的编程语言有自己专属的、能高效配置依赖关系的方法(譬如Ant)。
主要版本
make程序经历过各方多次的改写与重写,各方都依据自己的需要做了一些特定的改良。目前市面上主要流行有以下几种版本:

  • GNU make:GNU make对make的标准功能(通过clean-room工程)进行了重新改写,并加入作者自认为值得加入的新功能,常和GNU编译系统一起被使用,是大多数GNU Linux默认安装的工具。
  • BSD make:该版本是从Adam de Boor制作的版本上发展起来的。它在编译目标的时有并发计算的能力。主要应用于FreeBSD,NetBSD和OpenBSD这些系统。
  • Microsoft nmake:该版本主要用于微软的Windows系统中,需要注意的是,微软的nmake与Unix项目中的nmake是两种不同的东西,千万不要混淆。

makefile简单例子

1.输出Hello World!  hello.h文件、hello.c文件、main.c文件

//hello.h文件  
void hello (const char * name);  
  
//hello.c文件  
#include    
#include "hello.h"   
//语句 #include "FILE.h" 与 #include  有所不同:前者在搜索系统头文件目录之前将先在当前目录中搜索文件‘FILE.h’,  
//后者只搜索系统头文件而不查看当前目录。  
void hello(const char * name)   
{   
   printf("Hello, %s!\n", name);  
}  
  
//main.c文件  
#include "hello.h"  
int main(void)    
{    
   hello("World");    
   return 0;    
}
2.makefile文件
#注释标记#
#makefile文件
#main为目标文件(放在左边)  main.c、hello.c为依赖文件(放在‘ :’后边),main依赖于main.c、hello.c
main : main.c hello.c 
	gcc -o main main.c hello.c #注意命令前面一定要加Table键进行缩进,不能是空格
clean :
	rm -f main
3.命令执行,执行make命令会生成可执行文件main,执行main文件会输出Hello World!,执行make clean命令会删除main文件

makefile简单入门_第1张图片

makefile简介

Make从makefile(默认是当前目录下的名为‘Makefile’的文件)中读取项目的描述。makefile指定了一系列目标(比如可执行文件)和依赖(比如对象文件和源文件)的编译规则,其格式如下:

目标 :依赖
    命令
对每一个目标,make检查其对应的依赖文件修改时间来确定该目标是否需要利用对应的命令重新建立。注意到,makefile 中命令行必须以单个的 TAB 字符进行缩进,不能是空格。

GNU Make包含许多默认的规则(参考隐含规则)来简化makefile的构建。比如说,它们指定‘.o’文件可以通过编译‘.c’文件得到,可执行文件可以通过将‘.o’链接到一起获得。隐含规则通过被叫做make变量的东西所指定,比如 CC(C 语言编译器)和 CFLAGS(C程序的编译选项);在makefile文件中它们通过独占一行的 变量 = 值 的形式被设置。对 C++ ,其等价的变量是CXX和CXXFLAGS,而变量CPPFLAGS则是编译预处理选项。

#对上面的一个makefile文件可以重写如下:

CC = gcc
CFLAGS = -Wall

main: main.o hello.o
clean:
	rm -f main main.o hello.o

#该文件可以这样来读:使用C语言编译器gcc,和编译选项‘-Wall’,从对象文件‘main.o’和‘hello.o’生成
目标可执行文件main(文件‘main.o’和‘hello.o’通过隐含规则分别由‘main.c’和‘hello.c’生成)。
目标clean没有依赖文件,它只是简单地移除所有编译生成的文件。

执行make命令会在同级目录下生成hello.o、main.o和main这3个文件

makefile简单入门_第2张图片

一个源文件被修改要重新生成可执行文件,简单地再次输入 make 即可。通过检查目标文件和依赖文件的时间戳,程序 make 可识别哪些文件已经修改并依据对应的规则更新其对应的目标文件把main.c文件修改成如下

//main.c文件  
#include "hello.h"  
int main(void)    
{    
   hello("World,my God");    
   return 0;    
}

执行结果如下:

makefile简单入门_第3张图片

通过这两个makefile文件执行分析,我们可以看到若修改某文件,执行第二个makefile文件,只会对这个文件进行预处理、编译、汇编,与其他所有的目标文件链接生成可执行文件

一个专业的makefile文件通常包含用于安装(make install)和测试(make check)等额外的目标。本文中涉及到的例子都足够简单以至于可以完全不需要makefile,但是对任何大些的程序都使用make是很有必要的。

makefile文件使用变量

对上面的makefile可以重写如下

#指定编译器,c语言文件用gcc编译器或g++编译器,C++文件用g++编译器
cc=g++
 
#执行文件名
exe=main
 
# 目标文件
obj=main.o hello.o
 
# 生成可执行文件
$(exe):$(obj)
	$(cc) -o $(exe) $(obj)
 
# 依赖关系
main.o:main.c
	$(cc) -c main.c
hello.o:hello.c
	$(cc) -c hello.c
 
# make clean 用到
clean:
	rm -fr *.o $(exe)

另外,如果我们需要往工程中添加一个.c或.h,可能同时就要再手动为obj常量再添加第一个.o文件,如果这列表很长,代码会非常难看,为此,我们需要用到Makefile中的函数

#指定编译器,c语言文件用gcc编译器或g++编译器,C++文件用g++编译器  
cc = gcc

#执行文件名  
prom = hello

#依赖文件;.h头文件
deps = $(shell find ./ -name "*.h")
#源文件;.c文件
src = $(shell find ./ -name "*.c")
# 目标文件  
obj = $(src:%.c=%.o) 

#shell函数主要用于执行shell命令,具体到这里就是找出当前目录下所有的.c和.h文件

# 生成可执行文件  
$(prom): $(obj)
	$(cc) -o $(prom) $(obj)

#依赖关系  
%.o: %.c $(deps)
	$(cc) -c $< -o $@

#%.o:%.c,这是一个模式规则,表示所有的.o目标都依赖于与它同名的.c文件(当然还有deps中列出的头文件)
#命令部分的$<和$@,其中$<代表的是依赖关系表中的第一项
#(如果我们想引用的是整个关系表,那么就应该使用$^),具体到我们这里就是%.c。
#而$@代表的是当前语句的目标,即%.o。
#这样一来,make命令就会自动将所有的.c源文件编译成同名的.o文件。
#不用我们一项一项去指定了。整个代码自然简洁了许多。

clean:
	rm -rf $(obj) $(prom)

#其中,shell函数主要用于执行shell命令,具体到这里就是找出当前目录下所有的.c和.h文件。
#而$(src:%.c=%.o)则是一个字符替换函数,它会将src所有的.c字串替换成.o,
#实际上就等于列出了所有.c文件要编译的结果。
#有了这两个设定,无论我们今后在该工程加入多少.c和.h文件,Makefile都能自动将其纳入到工程中来。

 [C++基础]gcc的基本用法

你可能感兴趣的:(C++基础)