makefile学习

1,makefile文件的组成内容  

  • 显式规则
  • 隐式规则
  • 变量的意义
  • 文件指示
  • 注释

2,make工具的退出码

  • 0——表示makefile文件成功执行
  • 1——表示makefile文件执行时出现了错误
  • 2——如果用户使用了-q选项,并且make使一些目标不需要更新,则返回2

3,规则的使用

3.1,基本规则(显式规则)

  • a, targets : prerequisites
    • command
    • /…
  • b, targets : prerequisites ; command
    • command

    edit : main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o

    main.o : main.c defs.h
    cc -c main.c
    kbd.o : kbd.c defs.h command.h
    cc -c kbd.c
    command.o : command.c defs.h command.h
    cc -c command.c
    display.o : display.c defs.h buffer.h
    cc -c display.c
    insert.o : insert.c defs.h buffer.h
    cc -c insert.c
    search.o : search.c defs.h buffer.h
    cc -c search.c
    files.o : files.c defs.h buffer.h command.h
    cc -c files.c
    utils.o : utils.c defs.h
    cc -c utils.c
    clean :
    rm edit main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o

3.2,隐式规则

  • make工具有自己的推导规则,例如:make工具会自动使用gcc -c命令,将一个扩展名为.c的C语言源程序编译成一个同名的.o的目标文件。因此当编译一个单独的c文件到o文件时,可以使用隐含规则,让make工具自己推导规则。

    edit : main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o

    main.o : defs.h
    kbd.o : defs.h command.h
    command.o : defs.h command.h
    display.o : defs.h buffer.h
    insert.o : defs.h buffer.h
    search.o : defs.h buffer.h
    files.o : defs.h buffer.h command.h
    utils.o : defs.h
    clean :
    rm edit main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o

3.3,伪目标

  • 上面的makefile中,多次提到一个称为clean的目标,那就是一个伪目标。
  • 使用clean目标,执行后面的命令,清除所生成的目标文件,为下次编译工作做准备
  • 因此,make并不生成clean这个目标文件,这个文件称为伪目标文件。
  • 为了避免和已存在的文件重名,必须使用一个特殊的标记.PHONY来显示地指明一个目标
  • 是伪目标。如:.PHONY : clean.表示不管makefile文件是否有clean文件,都将其声明为
  • 一个伪目标。
  • 伪目标也可以作为默认目标,其位置必须是第一个目标

    all : main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o

    .PHONY all

    main.o : main.c defs.h
    cc -c main.c
    kbd.o : kbd.c defs.h command.h
    cc -c kbd.c
    command.o : command.c defs.h command.h
    cc -c command.c
    display.o : display.c defs.h buffer.h
    cc -c display.c
    insert.o : insert.c defs.h buffer.h
    cc -c insert.c
    search.o : search.c defs.h buffer.h
    cc -c search.c
    files.o : files.c defs.h buffer.h command.h
    cc -c files.c
    utils.o : utils.c defs.h
    cc -c utils.c

    这个makefile,默认目标为伪目标即不生成目标文件,所以这个makefile可以用来每次更新多个目标文件而生成目标文件。

3.4,使用通配符

  • make工具支持3种通配符*,?,[…].   ~表示用户的根目录,该目录通常都是用$HOME环境变量所保存。
  • *表示匹配所有字符,如*.c表示所有扩展名为c的文件。?表示匹配一个字符,[]表示一个模式。

3.5,搜索源文件

  1. 1,设置vpath变量

      1. makefile文件中使用特殊特殊变量vpath就是实现搜索源文件的功能,若没有指明该变量,make就默认在当前目录中去寻找依赖文件和目标文件。如vpath=src:../include。vpath变量被定义为2个目录,分别是src和../include。表示make工具会顺序搜索当前目录、当前目录下的src目录和父目录下include目录。

  1. 2,使用vpath关键字

    • 使用vpath的方法有三种:a,指定特定模式的文件的搜索目录vapth <pattern> <dirs>;b,指定文件的模式 vpath<pattern>表示清楚符合模式pattern的文件的搜索目录;c,清除所有的文件的搜索目录vpath.eg: vpath %.h ../include表示在当前目录和父目录下的include搜索所有的以.h结尾的文件

4,使用命令

4.1,显示命令

  1. make工具会把要执行的命令行在命令执行之前输出到屏幕上,当使用@字符在命令行前,这个命令将不被make工具显示出来,如:echo compling命令会先显示echo compling然后显示compling,而@echo compling不会显示echo compling而显示compling。
  2. 另外,make执行时,使用参数-n或者--just-print时,只是显示命令,不会执行命令。使用-s或者——slient,禁止所有命令的显示,不论命令前面是否有@。

4.2,执行命令

  1. makefile:
  2. all:
  3.         ls –l
  4.         ./hello
  5. .PHONY: all
  6. make –s all
  7. hello            hello.c      makefile
  8. hello world
  9. makefile:
  10. exec:
  11.          cd /home/admin
  12.          pwd
  13. .PHONY:  exec
  14. make –s exec
  15. /home/admin/make                    ####注意这里不是/home/admin
  16. makefile:
  17. exec:
  18.           cd /home/admin;pwd       ####如果需要让上一条命令的结果应用在下一条命令时,应该用
  19.                                                  ####   ;分割这两条命令
  20. .PHONY: exec
  21. make –s exec
  22. /home/admin/

4.3,命令出错

  1. 一般一个命令运行完成后,make工具就会检测每个命令的返回码。如果命令返回成功,make会执行下一条命令。当规则中所有的命令返回成功后,这个规则就运行完成。如果某个出现错误,make工具就会终止。但是在命令前加“-”可以忽略对该民联执行结果的判断。如:
  2. clean:
  3.         -rm –f *.c
  4. 或者make使用-i(--ignore-errors)是makefile所有命令都忽略错误。又或者规则以.IGNORE声明为目标。如:
  5. clean:
  6.           rm –f *.c
  7. .IGNORE

5,使用变量

5.1,使用普通变量

  1. 可以在makefile中使用变量,makefile中的变量代表一个字符串。makefile变量在声明的时候需要对其赋值,需要在变量前加上$符号(需要使用$符号,则用$$表示),对3.1中makefile使用变量后:
  2. objects=main.o kbd.o command.o display.o /
    insert.o search.o files.o utils.o edit : $(objects)
    gcc -o edit $(objects) main.o : main.c defs.h
    gcc -c main.c
    kbd.o : kbd.c defs.h command.h
    gcc -c kbd.c
    command.o : command.c defs.h command.h
    gcc -c command.c
    display.o : display.c defs.h buffer.h
    gcc -c display.c
    insert.o : insert.c defs.h buffer.h
    gcc -c insert.c
    search.o : search.c defs.h buffer.h
    gcc -c search.c
    files.o : files.c defs.h buffer.h command.h
    gcc -c files.c
    utils.o : utils.c defs.h
    gcc -c utils.c
    clean :
    rm edit $(objects)
    在很多情况下,用户将编译器以及编译器选项定义为变量,能够增强makefile的灵活性,便于移植。

    CC=gcc
    FLAGS=-c
    objects= main.o kbd.o command.o display.o\
            insert.o search.o files.o utils.o
    edit: $(objects)
        $(CC) -o edit $(objects)
    main.o: main.c defs.h
        $(CC) $(FLAGS) main.c
    kbd.o: kbd.c defs.h command.h
        $(CC) $(FLAGS) kbd.c
    command.o: command.c defs.h command.h
        $(CC) $(FLAGS) command.c
    display.o: display.c defs.h buffer.h
        $(CC) $(FLAGS) display.c
    insert.o: insert.c defs.h buffer.h
        $(CC) $(FLAGS) insert.c
    search.o:search.c defs.h buffer.h
        $(CC) $(FLAGS) search.c
    files.o:files.c defs.h buffer.h command.h
        $(CC) $(FLAGS) files.c
    utils.o: utils.c defs.h
        $(CC) $(FLAGS) utils.c
    clean :
        rm edit $(objects)
    .PHONY: clean

    当移植到其他的平台时,只需要更改变量CC和变量FLAGS即可,其他内容都不需要更改。

5.2,变量中的变量

  1. 在makefile中使用变量定义变量值的方式有3种
  2. 1,使用“=”操作符
  3. foo=$(bar)
  4. bar=$(ugh)
  5. ugh=Hub?
  6. all:
  7.       echo $(foo)
  8. 执行后显示,Hub?     PS:防止递归定义
  9. 2,使用:=操作符
  10. 使用:=操作符可以避免递归定义,使用:=定义时,只能使用前面已经定义好的变量。
  11. 3,使用?=操作符
  12. ?=表示如果变量之前未被定义,那么变量的值就被定义。如变量之前的值已经定义则赋值语句不做任何操作

5.3,追加变量的值

  1. makefile允许给变量追加一个值,操作符是+=
  2. objects=main.o foo.o bar.o utils.o
  3. objects+= $(objects) another.o
  4. 例子:
  5. CC =gcc
    FLAGS=-c
    FLAGS+= –O2
  6. objects =main.o kbd.o command.o display.o \
        insert.o search.o fles.o utils.o
    objects+=new.o
    edit: $(objects)
        $(CC) -o -O2 edit $(objects)
  7. main.o: main.c defs.h
        $(CC) $(FLAGS) main.c
       
  8. kbd.o: kbd.c defs.h command.h
        $(CC) $(FLAGS) kbd.c
    command.o: command.c defs.h command.h
        $(CC) $(FLAGS) command.c
    display.o: display.c defs.h buffer.h
        $(CC) $(FLAGS) display.c
    insert.o: insert.c defs.h buffer.h
        $(CC) $(FLAGS) insert.c
    search.o:search.c defs.h buffer.h
        $(CC) $(FLAGS) search.c
    files.o:files.c defs.h buffer.h command.h
        $(CC) $(FLAGS) files.c
    utils.o: utils.c defs.h
        $(CC) $(FLAGS) utils.c
    new.o: new.c
        $(CC) $(FLAGS) new.c
       
  9. clean :
        rm edit $(objects)
  10. .PHONY clean

5.4,自动化变量

  1. makefile一共有7个自动化变量$@,$%,$<,$?,$^,$+,$*。

    $@
        表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
    $%
        仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
    $<
        依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
    $?
        所有比目标新的依赖目标的集合。以空格分隔。
    $^
        所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
    $+
        这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
    $* 
       这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。

6,使用条件判断

6.1,条件表达式

  1. 条件关键字有4个:ifeq,ifneq,ifdef,ifndef
  2. 1,ifeq:表示相等则执行,其格式如下:
  3.   ifeq(<arg1>,<arg2>)
  4.   ifeq ‘<arg1>’’<arg2>’
  5.   ifeq “<arg1>”“<arg2>”
  6.   ifeq “<arg1>”’<arg2>’
  7.   ifeq ‘<arg1>’”<arg2>”
  8. 2,ifneq:表示不相等则执行
  9.   ifeq(<arg1>,<arg2>)
  10.   ifeq ‘<arg1>’’<arg2>’
  11.   ifeq “<arg1>”“<arg2>”
  12.   ifeq “<arg1>”’<arg2>’
  13.   ifeq ‘<arg1>’”<arg2>”
  14. 3,ifdef:表示值非空则执行
  15.   ifdef <variable-name>
  16. 4,ifndef:表示值为空则执行
  17.   ifndef<variable-name>
  18. 例子:
  19. var1 =
    var2 = hello
  20. all:
    ifdef $var1
        $var1=hello
    endif
  21. ifeq($(var1),$(var2))
        echo "they are equal!"
    else
  22.     echo "they are not equal"
    endif
    .PHONY:all

    6.2,表达式实例

      CC=gcc

      libs_for_gpu=-lgpu
      normal_libs=
      objects= main.o list.o my_lib.o
      app: $(objects)
          ifeq ($(CC),gcc)
              $(CC) -o app $(objects) $(libs_for_gpu)
          else
              $(CC) -o app $(objects) $(normal_libs)
          endif

      main.o : main.c
          $(CC) -c main.c
      list.o : list.c
          $(CC) -c list.c
      my_lib.o : my_lib.c
          $(CC) -c my_lib.c

    7,使用函数

    7.1,函数调用的语法

    1. makefile文件中的函数以$标识,其语法如下:
    2. $(<function> <arguments>)
    3. or
    4. ${<function> <arguments>}
    5. <function>表示函数名,<arguments>表示函数的参数列表。参数间以逗号”,”分隔,函数名与参数间用空格分隔。例子:
    6. comma :=,
      empty :=
      space := $(empty) $(empty)
      var :=a b c
      bar :=
      all:
          $bar:=$(subst $(space),$(var),$(comma))
      .PHONY: all

    7.2,字符串处理函数

      1. $(subst <from>;,<to>;,<text>;)
      2. $(subst ee,EE,feet on the street) 把”feet on the street”中的“ee“替换成“EE“
      3. $(patsubst <pattern>;,<replacement>;,<text>;) 
      4. $(patsubst %.c,%.o,x.c.c bar.c) 把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
      5. $(strip <string>;)去空格函数
      6. 去掉<string>字串中开头和结尾的空字符。
      7. $(strip a b c ) 把字串“a b c ”去到开头和结尾的空格,结果是“a b c”
      8. $(findstring <find>,<in>;)查找字符串函数
      9. 在字串<in>中查找<find>;字串。
      10. $(findstring a,a b c) 
        $(findstring a,b c) 第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
      11. $(filter <pattern...>,<text>)过滤函数
      12. 以<pattern>;模式过滤<text>;字符串中的单词,保留符合模式<pattern>;的单词。可以有多个模式。
    1. sources := foo.c bar.c baz.s ugh.h 
      foo: $(sources) 
              cc $(filter %.c %.s,$(sources)) -o foo
    2. $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。
    3.  
      $(filter-out <pattern...>,<text>;) 反过滤函数
    4. 以<pattern>模式过滤<text>;字符串中的单词,去除符合模式<pattern>;的单词。可以有多个模式。
    5. objects=main1.o foo.o main2.o bar.o 
              mains=main1.o main2.o  
              $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”
    6. $(sort <list>) 排序函数
    7. 给字符串<list>;中的单词排序(升序)。$(sort foo bar lose)返回“bar foo lose” 。
    8. $(word <n>,<text>;)取单词函数
    9. 取字符串<text>中第<n>个单词。(从一开始) $(word 2, foo bar baz)返回值是“bar”。
    10. $(wordlist <s>,<e>,<text>;)取单词串函数
    11.            从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
    12.            $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
    13. $(words <text>) 单词个数统计函数
    14.           统计<text>;中字符串中的单词个数,$(words, foo bar baz)返回值是“3”
    15. $(firstword <text>)首单词函数
    16.            取字符串<text>中的第一个单词。$(firstword foo bar)返回值是“foo”。

    7.3,文件名操作函数

    1. $(dir <names…>),从文件名序列<names>中取出目录部分
    2. result:=
      all:
          $(result)=$(dir test.c /home/admin/test.c)
          echo -n "the result is :"
          echo $(result)
      .PHONY: all
    3. 结果是./ /home/admin
    4. $(notdir <names…>)从文件名序列<names…>中取出非目录部分。非目录部分是指最后一个“/”之后的部分
    5. $(result)=$(notdir test.c /home/admin/test.c)
    6. result为test.c test.c
    7. $(suffix <name…>)取后缀函数
    8. $(result)=$(suffix test.txt test.c)得到.txt .c
    9. $(basename <names…>)从文件名序列取各个文件名的前缀部分
    10. $(addsuffix <suffix>,<names…>) 把后缀<suffix>加到序列中每个单词的后面
    11. $(addprefix <prefix>,<names…>)把前缀<prefix>加到序列中每个单词的前面
    12. $(join <list1>,<list2>)把<list2>中的单词对应地加到<list1>的单词的后面

    7.4,foreach函数

    1. foreach函数是用来控制循环。$(foreach <var> ,<list> ,<text>) <var>最好是一个变量名,<list>可以是一个表达式,而<text>一般会使用<var>参数来一次枚举<list>中的单词。
    2. files :=
      names := a b c d
    3. all:
          $(files) :=$(foreach n, $(names),$(n).c)
          echo -n "the file is : "
          echo $(files)
      .PHONY: all

    7.5,if 函数

    1. if函数很像makefile文件中所支持的条件语句——ifeq。$(if <condition>,<then-part>) or $(if <condition> ,<then-part> ,<else-part>)
    2. a := 1
      b := 1
      result :=
      all:
          $(result) :=$(if ifeq($a,$b), $a=10,$b=10)
      echo -n "the result is :"
      echo $(result)
      .PHONY: all

    7.6,call 函数

    1. call函数为用户创建一个自己定义的函数。用户可以写一个非常复杂的表达式,每次用到这个表达式的时候,使用call函数跳转到该表达式出执行即可。$(call <expression> ,<parm1> ,<parm2> ,<parm3> …),当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等会被参数<parm1>, <parm2>,<parm3>依次取代。
    2. add = $(1)+$(2)
      result :=
      all:
      $(result) := $(call add,1,2)
      echo -n "the result is : "
      echo $(result)
      .PHONY: all

    7.7,origin函数

    1. origin函数操作变量的值,该函数可以将变量的定义返回给用户,其语法如下所示:$(origin <variable>) <variable>表示变量的名字。

    7.8,shell函数

    1. shell函数用来执行操作系统shell的命令。该函数把执行操作系统命令后的输出作为函数返回。用户可以通过调用shell函数,使用操作系统命令来生成一个变量。格式如下:
    2. $(shell <command> ,<parm1> ,<parm2> ,<parm3>…) <command>表示需要执行的shell的命令,所以该函数的返回值为执行的shell命令的输出结果。
    3. files :=
      all:
      $(files) := $(shell ls)
      echo -n "the files is : "
      echo $(files)
      .PHONY: all

    8,makefile实例

    8.1 makefile实例——项目中的总makefile

    1. 若某个项目的源代码按模块分类分别存储在多个目录下,每个模块的目录内都有各自的的makefile文件。其源程序存储的目录结构如下所示:
    2. hello(目录)     include(目录)    list(目录)     Makefile(makefile文件)
    3. project/hello目录下。
    4. hello.c(源代码文件)      Makefile(makefile文件)
    5. project/include目录下。
    6. hello.h(头文件)     list.h(头文件)
    7. project/list目录下。
    8. list.c(源代码文件)     Makefile(makefile文件)
    9. hello目录下的makefile如下:
    10. hello: hello.o
          gcc hello.o -o hello
      hello: hello.c
          hcc -c hello.c
      clean:
          rm -rf hello *.c
      .PHONY: clean
    11. list目录下的makefile文件如下:
    12. list: list.o
          gcc list.o -o list
      list.o: list.c
          gcc -c list.o
      clean:
          rm -rf list *.o
      .PHONY: clean
    13. project目录中的makefile文件如下:
    14. SUBDIRS := list hello
      all: modules
      .PHONY all
      modules:
          for n in $(SUBDIRS)
          do
              exit= make --directory=$$n

              if [$exit="1"];then
                  exit 1
              fi
          done
      .PHONY modules
      clean:
          for n in $(SUBDIRS)
          do
              make --directory=$$n clean
          done
      .PHONY: clean

    8.2 makefile实例——makefile模板

    1. 一个比较规范的makefile文件模板:
    2. CC = XXX-gcc
      CFLAGS+= xxx
      LDFLAGS+= xxx
      EXEC = xxx
    3. all: $(EXEC)
      .PHONY all
    4. xxx: xxx.c
          $(CC) $(CFLAGS) $(LDFLAGS) xxx.c -o $@
    5. clean:
          -rm -f $(EXEC) *.elf *.gdb *.o *.a
      .PHONY: clean
    6. 使用该模板创建hello程序的makefile文件:
    7. CC = gcc
      CFLAGS+=
      LDFLAGS+=
      EXEC =
    8. all: $(EXEC)
      .PHONY all

      hello: hello.c
          $(CC) $(CFLAGS) $(LDFLAGS) hello.c -o $@

      clean:
          -rm -f $(EXEC) *.elf *.gdb *.o *.a
      .PHONY: clean

      如果此时该程序需要使用pthread线程库,并且使用做高的优化编译选项O3,则修改后的makefile文件如下:

      CC = gcc
      CFLAGS+= -O3
      LDFLAGS+= -pthread
      EXEC = hello

      all: $(EXEC)
      .PHONY all

      hello: hello.c
          $(CC) $(CFLAGS) $(LDFLAGS) hello.c -o $@

      clean:
          -rm -f $(EXEC) *.elf *.gdb *.o *.a
      .PHONY: clean


     



    posted @ 2013-11-18 21:57  小石头@shu  阅读(279)  评论(0编辑  收藏  举报