• 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


       



    23. 相关阅读:
      P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II
      P4887 【模板】莫队二次离线(第十四分块(前体))
      SP20644 ZQUERY
      企业战略管理【0612】
      管理经济学【0812】
      商务沟通[0664]
      国际企业管理【0813】
      社区管理【0272】
      战略管理【1059】
      管理沟通【1279】
    24. 原文地址:https://www.cnblogs.com/burness/p/3430330.html
    Copyright © 2020-2023  润新知