一、make及Makefile概述
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有该命令,比如VC++中的nmake,linux下GNU的make,Delphi中的make.简而言之,make命令执行时,需要一个makefile文件,以告诉make命令需要怎么样的去编译和链接程序.
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有该命令,比如VC++中的nmake,linux下GNU的make,Delphi中的make.简而言之,make命令执行时,需要一个makefile文件,以告诉make命令需要怎么样的去编译和链接程序.
编写的Makefile文件需要一定的规则:
目标(target):依赖文件列表(prerequisites)
<tab>命令列表(command)
<1>target可以是一个.obj文件,也可以是执行文件,还可以是一个标签(label).
<2>prerequisites就是要生成target所需要的文件或目标,可以是.c和.h文件.
<3>command定义了如何生成目标文件的操作系统命令,即任意的shell命令.它是命令行,可以有多条命令.
其中,"#"表示注释,"/"表示换行.
注意,Makefile中的clean不是一个文件,它只是一个动作的名字,类似于c语言中的lale,其冒号后什么都没有.因此,执行make时,就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令.要执行其后的命令,就需要在make命令后明显的指出这个label的名字.
二、Makefile文件名
默认情况下,make命令会在当前目录下按顺序找文件名为"Makefile"、"makefile".当然,用户也可以使用别的文件名来书写makefile,比如make.linux、makefile2,但在使用make时必须加上参数-f.即: make -f makefile2
三、Makefile重点说明
(1)创建源文件:
test.h :
#ifndef __TEST_H__
#define __TEST_H__
#include <stdio.h>
extern void test1(void);
extern void test2(void);
extern void test3(void);
endif
test1.c :
#include "test.h"
void test1(void)
{
printf("this is a test1 program!/n");
}
test2.c :
#include "test.h"
void test1(void)
{
printf("this is a test2 program!/n");
}
test3.c :
#include "test.h"
void test3(void)
{
printf("this is a test3 program!/n");
}
main.c :
#include "test.h"
int main(void)
{
test1();
test2();
test3();
return 0;
}
[root@localhost /]#ls
[root@localhost /]#test1.c test2.c test3.c main.c test.h
(2)利用make编译程序时,产生的错误主要分为两种:
1.一种是makefile错误:
比如:当头文件不在当前路径下时,但编写的Makefile文件时,若写成main.o:main.c test.h,则makefile肯定报错,这是因为在Makefile中"main.o:main.c test.h"表示main.o依赖于当前路径下的main.o、test.h,但头文件不在当前路径下,所以Makefile会报错.
2.一种是gcc错误:
gcc报错与Makefile报错不同,前者是编译时报错,后者多数是书写规则或没有找到依赖文件而报错.由此可以看出,make并不管命令是如何工作的,而只是根据Makefile中所定义的依赖关系,去寻找相应的.o文件或.h文件.
(3)简而言之,无论文件在什么路径下,只要按照Makefile的规则来写,Makefile肯定不会报错.另外,在gcc时指定.c文件、.h文件、.o文件、libxxx.so文件、libxxx.a文件的路径,gcc也肯定不会报错.这样一来,一个正确的Makefile便写成了.
四、基础例程
(1)所有文件都在当前路径下:
Makefile :
main:main.o test1.o test2.o test3.o #这一行不要加头文件,因目标文件main只是链接4个.o文件即可生成
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c test.h #在Makefile中定义了main.o依赖于test.h,所以test.h必须在当前路径下
gcc -c main.c -o main.o
test1.o:test1.c test.h
gcc -c test1.c -o test1.o #gcc必须使用参数-c,这样才能生成.o文件(这只是编译,而不是链接)
test2.o:test1.c test.h
gcc -c test2.c -o test2.o
test3.o:test1.c test.h
gcc -c test3.c -o test3.o
clean:
rm -rf *.o
allclean:
rm -rf *.o main
说明:
目标(target):依赖文件列表(prerequisites)
<tab>命令列表(command)
<1>target可以是一个.obj文件,也可以是执行文件,还可以是一个标签(label).
<2>prerequisites就是要生成target所需要的文件或目标,可以是.c和.h文件.
<3>command定义了如何生成目标文件的操作系统命令,即任意的shell命令.它是命令行,可以有多条命令.
其中,"#"表示注释,"/"表示换行.
注意,Makefile中的clean不是一个文件,它只是一个动作的名字,类似于c语言中的lale,其冒号后什么都没有.因此,执行make时,就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令.要执行其后的命令,就需要在make命令后明显的指出这个label的名字.
二、Makefile文件名
默认情况下,make命令会在当前目录下按顺序找文件名为"Makefile"、"makefile".当然,用户也可以使用别的文件名来书写makefile,比如make.linux、makefile2,但在使用make时必须加上参数-f.即: make -f makefile2
三、Makefile重点说明
(1)创建源文件:
test.h :
#ifndef __TEST_H__
#define __TEST_H__
#include <stdio.h>
extern void test1(void);
extern void test2(void);
extern void test3(void);
endif
test1.c :
#include "test.h"
void test1(void)
{
printf("this is a test1 program!/n");
}
test2.c :
#include "test.h"
void test1(void)
{
printf("this is a test2 program!/n");
}
test3.c :
#include "test.h"
void test3(void)
{
printf("this is a test3 program!/n");
}
main.c :
#include "test.h"
int main(void)
{
test1();
test2();
test3();
return 0;
}
[root@localhost /]#ls
[root@localhost /]#test1.c test2.c test3.c main.c test.h
(2)利用make编译程序时,产生的错误主要分为两种:
1.一种是makefile错误:
比如:当头文件不在当前路径下时,但编写的Makefile文件时,若写成main.o:main.c test.h,则makefile肯定报错,这是因为在Makefile中"main.o:main.c test.h"表示main.o依赖于当前路径下的main.o、test.h,但头文件不在当前路径下,所以Makefile会报错.
2.一种是gcc错误:
gcc报错与Makefile报错不同,前者是编译时报错,后者多数是书写规则或没有找到依赖文件而报错.由此可以看出,make并不管命令是如何工作的,而只是根据Makefile中所定义的依赖关系,去寻找相应的.o文件或.h文件.
(3)简而言之,无论文件在什么路径下,只要按照Makefile的规则来写,Makefile肯定不会报错.另外,在gcc时指定.c文件、.h文件、.o文件、libxxx.so文件、libxxx.a文件的路径,gcc也肯定不会报错.这样一来,一个正确的Makefile便写成了.
四、基础例程
(1)所有文件都在当前路径下:
Makefile :
main:main.o test1.o test2.o test3.o #这一行不要加头文件,因目标文件main只是链接4个.o文件即可生成
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c test.h #在Makefile中定义了main.o依赖于test.h,所以test.h必须在当前路径下
gcc -c main.c -o main.o
test1.o:test1.c test.h
gcc -c test1.c -o test1.o #gcc必须使用参数-c,这样才能生成.o文件(这只是编译,而不是链接)
test2.o:test1.c test.h
gcc -c test2.c -o test2.o
test3.o:test1.c test.h
gcc -c test3.c -o test3.o
clean:
rm -rf *.o
allclean:
rm -rf *.o main
说明:
<1>在Makefile中,只有最终的可执行文件(如main)下的gcc命令是链接过程,链接过程只链接.o文件,因此最终的可执行文件(如main)所依赖的文件中不能包含test.h文件.而其它的中间目标(main.o、test1.o、test2.o、test3.o)下的gcc命令只是编译过程,因此要加-c参数.
<2>由于test1.o依赖于test1.c和test.h,因此写成"test1.o:test1.c test.h",但这种写法要求test1.c和test.h必须在当前路径下.
(2)头文件在系统路径下,其它文件均在当前路径下:
[root@localhost /]#mv test.h /usr/include
Makefile :
main:main.o test1.o test2.o test3.o
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c #头文件不在当前路径下,所以依赖文件中一定不能写test.h
gcc -c main.c -o main.o 或写成: gcc -c test1.c -o main.o -I/usr/include
test1.o:test1.c
gcc -c test1.c -o test1.o 或写成: gcc -c test1.c -o test1.o -I/usr/include
test2.o:test1.c
gcc -c test2.c -o test2.o 或写成: gcc -c test2.c -o test2.o -I/usr/include
test3.o:test1.c
gcc -c test3.c -o test3.o 或写成: gcc -c test3.c -o test3.o -I/usr/include
clean:
rm -rf *.o
allclean:
rm -rf *.o main
<2>由于test1.o依赖于test1.c和test.h,因此写成"test1.o:test1.c test.h",但这种写法要求test1.c和test.h必须在当前路径下.
(2)头文件在系统路径下,其它文件均在当前路径下:
[root@localhost /]#mv test.h /usr/include
Makefile :
main:main.o test1.o test2.o test3.o
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c #头文件不在当前路径下,所以依赖文件中一定不能写test.h
gcc -c main.c -o main.o 或写成: gcc -c test1.c -o main.o -I/usr/include
test1.o:test1.c
gcc -c test1.c -o test1.o 或写成: gcc -c test1.c -o test1.o -I/usr/include
test2.o:test1.c
gcc -c test2.c -o test2.o 或写成: gcc -c test2.c -o test2.o -I/usr/include
test3.o:test1.c
gcc -c test3.c -o test3.o 或写成: gcc -c test3.c -o test3.o -I/usr/include
clean:
rm -rf *.o
allclean:
rm -rf *.o main
说明:由于头文件不在当前路径下时,因此依赖关系就不能再写成testx.o:testx.c test.h了,因为当前路径下没有该头文件,这是由Makefile书写规则所决定的.例如,当写成test1.o:test1.c test.h时,make会在当前路径下寻找test.h,如果找不到,Makefile就报错.
(3)头文件在任意路径下,有两种处理方法:
[root@localhost /]#mv test.h /home/lishuai/haha
处理方法一:
Makefile :
main:main.o test1.o test2.o test3.o
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c #头文件不在当前路径下,所以依赖文件中一定不能写test.h
gcc -c main.c -o main.o -I/home/lishuai/haha
test1.o:test1.c
gcc -c test1.c -o test1.o -I/home/lishuai/haha
test2.o:test1.c
gcc -c test2.c -o test1.o -I/home/lishuai/haha
test3.o:test1.c
gcc -c test3.c -o test1.o -I/home/lishuai/haha
clean:
rm -rf *.o
allclean:
rm -rf *.o main
处理方法二:
将每个.c文件(test1.c、test2.c、test3.c、main.c)中的头文件进行如下处理:
#include "./haha/test.h"
Makefile:
main:main.o test1.o test2.o test3.o
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c test.h
gcc -c main.c -o main.o
test1.o:test1.c
gcc -c test1.c -o test1.o 不能再写成(否则报错): gcc -c test1.c -o test1.o -I/home/lishuai/haha
test2.o:test1.c
gcc -c test2.c -o test2.o 不能再写成(否则报错): gcc -c test2.c -o test1.o -I/home/lishuai/haha
test3.o:test1.c
gcc -c test3.c -o test3.o 不能再写成(否则报错): gcc -c test3.c -o test1.o -I/home/lishuai/haha
clean:
rm -rf *.o
allclean:
rm -rf *.o main
五、中级例程
(1)所有文件都在当前路径下:
Makefile :
object=main.o test1.o test2.o test3.o
cc=gcc
flags=-O2 -Wall -g
main:$(object) #这一行不需要加头文件,因为目标文件main只是链接4个.o文件即可生成
$(cc) -o main $(object) $(flags)
main.o:main.c test.h
$(cc) -c main.c -o main.o $(flags)
test1.o:test1.c test.h #头文件test.h在当前路径下,所以要写上
$(cc) -c test1.c -o test1.o $(flags) #gcc必须使用参数-c,这样才能生成.o文件
test2.o:test1.c test.h
$(cc) -c test2.c -o test2.o $(flags)
test3.o:test1.c test.h
$(cc) -c test3.c -o test3.o $(flags)
.PHONY:clean
clean:
-rm main $(object)
说明:
<1>定义变量object为main.o test1.o test2.o test3.o,类似于c语言中的宏定义.当用户需要使用main.o test1.o test2.o test3.o时,可以直接引用变量object,当然引用的方法是:$(object).即在Makefile中可以使用$(object)来替代main.o test1.o test2.o test3.o
<2>定义变量cc为gcc,类似于c语言中的宏定义.当用户需要使用编译器进行编译时,可以直接引用变量cc,当然引用的方法是:$(cc).这种编译器的替换,主要是为了方便将编译器由gcc更改为arm-linux-gcc.
<3>清空目标文件时,多采用上面的写法:
.PHONY表示clean是一个伪目标;rm命令前的减号表示强制进行后面的操作.
<4>make实现的自推到
make功能很强大,可以自动推导文件及文件依赖关系后面的命令.所以,只要make看到.o文件,它就会自动的将.c文件加在依赖关系中,并且后面的命令也被自动推导出来了.
Makefile :
object=main.o test1.o test2.o test3.o
main:$(object)
main.o:main.c test.h
test1.o:test.h #头文件test.h在当前路径下,所以要写上
test2.o:test.h
test3.o:test.h
.PHONY:clean
clean:
-rm main $(object)
(2)将test1.c、test2.c、test3.c制作为共享库:
注意,制作动态库时,共享库需要的所有.c文件和.h文件都必须在同一路径下.而在链接共享库时,头文件和动态库可以在不同的路径下.
[root@localhost /]#gcc test1.c test2.c test3.c -shared -o libhehe.so
这样就制作完成了动态链接库.
Makefile :
object=main.o
cc=gcc
flags=-O2 -Wall -g
main:$(object)
$(cc) -o main $(object) $(flags) -L./ -lhehe
main.o:main.c
$(cc) -c -o $(object) main.c
.PHONY:clean
clean:
-rm main $(object)
说明:
无论是静态库还是共享库,都只是在链接时使用,而不是其它阶段.因此,只有在
main:$(object)
$(cc) -o main $(object) $(flags) -L./ -lhehe
中加入库的路径以及库的名称,
而在编译阶段
testx.o:testx.c test.h
$(cc) -c testx.c -o testx.o $(flags)
则不需要指定库的路径以及库的名称.
(3)当将test1.c、test2.c制作为共享库1,再将test3.c制作为共享库2:
[root@localhost /]#gcc test1.c test2.c -shared -o libhehe1.so
[root@localhost /]#gcc test3.c -shared -o libhehe2.so
Makefile :
object=main.o
cc=gcc
flags=-O2 -Wall -g
main:$(object)
$(cc) -o main $(object) $(flags) -L./ -lhehe1 -L./ -lhehe2
main.o:main.c
$(cc) -c -o $(object) main.c $(flags)
.PHONY:clean
clean:
-rm main $(object)
<4>当test1.c、test2.c、test3.c、test.h在当前路径下,main.c在其它路径下时:
[root@localhost lishuai]#mv main.c ../
[root@localhost lishuai]#pwd
/home/lishuai
Makefile :
object=main.o test1.o test2.o test3.o
cc=gcc
dir=test.h
flags=-O2 -Wall -g
main:$(object)
$(cc) -o main $(object) $(flags)
main.o:/home/main.c /home/lishuai/test.h
$(cc) -c /home/main.c -o main.o $(flags) -I/home/lishuai
test1.o:test1.c $(dir)
$(cc) -c test1.c -o test1.o $(flags)
test2.o:test1.c $(dir)
$(cc) -c test2.c -o test2.o $(flags)
test3.o:test1.c $(dir)
$(cc) -c test3.c -o test3.o $(flags)
.PHONY:clean
clean:
-rm main $(object)
六、高级例程
(1)所有文件都在当前路径下:
Makefile:
object=main.o test1.o test2.o test3.o
(或写成:object=main.o test1.o test2.o
object += test3.o)
cc=gcc
flags=-O2 -Wall -g
main:$(object)
$(cc) $(object) -o main $(flags)
$(object):%o:%c
$(cc) -c $< -o $@
.PHONE:clean
clean:
-rm *.o main
(3)头文件在任意路径下,有两种处理方法:
[root@localhost /]#mv test.h /home/lishuai/haha
处理方法一:
Makefile :
main:main.o test1.o test2.o test3.o
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c #头文件不在当前路径下,所以依赖文件中一定不能写test.h
gcc -c main.c -o main.o -I/home/lishuai/haha
test1.o:test1.c
gcc -c test1.c -o test1.o -I/home/lishuai/haha
test2.o:test1.c
gcc -c test2.c -o test1.o -I/home/lishuai/haha
test3.o:test1.c
gcc -c test3.c -o test1.o -I/home/lishuai/haha
clean:
rm -rf *.o
allclean:
rm -rf *.o main
处理方法二:
将每个.c文件(test1.c、test2.c、test3.c、main.c)中的头文件进行如下处理:
#include "./haha/test.h"
Makefile:
main:main.o test1.o test2.o test3.o
gcc main.o test1.o test2.o test3.o -o main
main.o:main.c test.h
gcc -c main.c -o main.o
test1.o:test1.c
gcc -c test1.c -o test1.o 不能再写成(否则报错): gcc -c test1.c -o test1.o -I/home/lishuai/haha
test2.o:test1.c
gcc -c test2.c -o test2.o 不能再写成(否则报错): gcc -c test2.c -o test1.o -I/home/lishuai/haha
test3.o:test1.c
gcc -c test3.c -o test3.o 不能再写成(否则报错): gcc -c test3.c -o test1.o -I/home/lishuai/haha
clean:
rm -rf *.o
allclean:
rm -rf *.o main
五、中级例程
(1)所有文件都在当前路径下:
Makefile :
object=main.o test1.o test2.o test3.o
cc=gcc
flags=-O2 -Wall -g
main:$(object) #这一行不需要加头文件,因为目标文件main只是链接4个.o文件即可生成
$(cc) -o main $(object) $(flags)
main.o:main.c test.h
$(cc) -c main.c -o main.o $(flags)
test1.o:test1.c test.h #头文件test.h在当前路径下,所以要写上
$(cc) -c test1.c -o test1.o $(flags) #gcc必须使用参数-c,这样才能生成.o文件
test2.o:test1.c test.h
$(cc) -c test2.c -o test2.o $(flags)
test3.o:test1.c test.h
$(cc) -c test3.c -o test3.o $(flags)
.PHONY:clean
clean:
-rm main $(object)
说明:
<1>定义变量object为main.o test1.o test2.o test3.o,类似于c语言中的宏定义.当用户需要使用main.o test1.o test2.o test3.o时,可以直接引用变量object,当然引用的方法是:$(object).即在Makefile中可以使用$(object)来替代main.o test1.o test2.o test3.o
<2>定义变量cc为gcc,类似于c语言中的宏定义.当用户需要使用编译器进行编译时,可以直接引用变量cc,当然引用的方法是:$(cc).这种编译器的替换,主要是为了方便将编译器由gcc更改为arm-linux-gcc.
<3>清空目标文件时,多采用上面的写法:
.PHONY表示clean是一个伪目标;rm命令前的减号表示强制进行后面的操作.
<4>make实现的自推到
make功能很强大,可以自动推导文件及文件依赖关系后面的命令.所以,只要make看到.o文件,它就会自动的将.c文件加在依赖关系中,并且后面的命令也被自动推导出来了.
Makefile :
object=main.o test1.o test2.o test3.o
main:$(object)
main.o:main.c test.h
test1.o:test.h #头文件test.h在当前路径下,所以要写上
test2.o:test.h
test3.o:test.h
.PHONY:clean
clean:
-rm main $(object)
(2)将test1.c、test2.c、test3.c制作为共享库:
注意,制作动态库时,共享库需要的所有.c文件和.h文件都必须在同一路径下.而在链接共享库时,头文件和动态库可以在不同的路径下.
[root@localhost /]#gcc test1.c test2.c test3.c -shared -o libhehe.so
这样就制作完成了动态链接库.
Makefile :
object=main.o
cc=gcc
flags=-O2 -Wall -g
main:$(object)
$(cc) -o main $(object) $(flags) -L./ -lhehe
main.o:main.c
$(cc) -c -o $(object) main.c
.PHONY:clean
clean:
-rm main $(object)
说明:
无论是静态库还是共享库,都只是在链接时使用,而不是其它阶段.因此,只有在
main:$(object)
$(cc) -o main $(object) $(flags) -L./ -lhehe
中加入库的路径以及库的名称,
而在编译阶段
testx.o:testx.c test.h
$(cc) -c testx.c -o testx.o $(flags)
则不需要指定库的路径以及库的名称.
(3)当将test1.c、test2.c制作为共享库1,再将test3.c制作为共享库2:
[root@localhost /]#gcc test1.c test2.c -shared -o libhehe1.so
[root@localhost /]#gcc test3.c -shared -o libhehe2.so
Makefile :
object=main.o
cc=gcc
flags=-O2 -Wall -g
main:$(object)
$(cc) -o main $(object) $(flags) -L./ -lhehe1 -L./ -lhehe2
main.o:main.c
$(cc) -c -o $(object) main.c $(flags)
.PHONY:clean
clean:
-rm main $(object)
<4>当test1.c、test2.c、test3.c、test.h在当前路径下,main.c在其它路径下时:
[root@localhost lishuai]#mv main.c ../
[root@localhost lishuai]#pwd
/home/lishuai
Makefile :
object=main.o test1.o test2.o test3.o
cc=gcc
dir=test.h
flags=-O2 -Wall -g
main:$(object)
$(cc) -o main $(object) $(flags)
main.o:/home/main.c /home/lishuai/test.h
$(cc) -c /home/main.c -o main.o $(flags) -I/home/lishuai
test1.o:test1.c $(dir)
$(cc) -c test1.c -o test1.o $(flags)
test2.o:test1.c $(dir)
$(cc) -c test2.c -o test2.o $(flags)
test3.o:test1.c $(dir)
$(cc) -c test3.c -o test3.o $(flags)
.PHONY:clean
clean:
-rm main $(object)
六、高级例程
(1)所有文件都在当前路径下:
Makefile:
object=main.o test1.o test2.o test3.o
(或写成:object=main.o test1.o test2.o
object += test3.o)
cc=gcc
flags=-O2 -Wall -g
main:$(object)
$(cc) $(object) -o main $(flags)
$(object):%o:%c
$(cc) -c $< -o $@
.PHONE:clean
clean:
-rm *.o main