现在的位置: 首页 > 综合 > 正文

make及makefile

2018年04月17日 ⁄ 综合 ⁄ 共 7860字 ⁄ 字号 评论关闭
一、make及Makefile概述
    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
   说明:
     <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
    说明:由于头文件不在当前路径下时,因此依赖关系就不能再写成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

抱歉!评论已关闭.