I.MX6ULL_Linux_基础篇(4) linux下代码编辑与编译 Makefile

        对mcu编程时,我们通常在win系统下,使用集成IDE如keil,IAR进行代码的编辑与编译。但是在linux系统下,并没有一个集成了所有功能的软件供大家使用。前面给大家介绍的shell脚本编写均建立在图形化linux系统下使用记事本gedit工具完成,那linux有没有专门的代码编辑工具呢?通常大家会使用vim进行编辑,但有一说一,vim没有界面,新手使用也并不友好,偶尔应急还好,但是作用于大工程的编写实在麻烦。(这里可以提前看第5篇开发工具和开发方法的介绍)!编辑工具有了,那我们如何进行编译呢,使用gcc编译器,该工具是使用命令行对需要编译的代码进行操作。linux下的工程通常是很大的,我们还要指定编译规则,这就是Makefile的作用,使用Makefile时需要用到make工具。

        本篇主要是对Makefile进行学习记录!

Makefile基本语法

    目标:依赖

    tab    命令

        目标:一般指要编译的目标,也可以是一个动作。

        依赖:指执行当前目标所需要的项,包括其他目标,某个具体的文件或库等,一个目标可以有多个依赖。

        命令:该目标下具体要执行的命令,可以没有,也可以有多条。多条时,每条命令一行。

        注:如果没有指定目标,则默认执行第一个目标,Makefile中的注释使用#开头,不能使用C中的//或/* */,在 Makefile 中的命令,必须要以[Tab]键开始。

make常用选项

用法:make [选项] [目标] ...
选项:
  -b, -m                      忽略兼容性。
  -B, --always-make           无条件 make 所有目标。
  -C DIRECTORY, --directory=DIRECTORY
                              在执行前先切换到 DIRECTORY 目录。
  -d                          打印大量调试信息。
  --debug[=FLAGS]             打印各种调试信息。
  -e, --environment-overrides
                              环境变量覆盖 makefile 中的变量。
  --eval=字串               将 <字串> 作为 makefile 语句估值。
  -f FILE, --file=FILE, --makefile=FILE
                              从 FILE 中读入 makefile。
  -h, --help                  打印该消息并退出。
  -i, --ignore-errors         忽略来自命令配方的错误。
  -I DIRECTORY, --include-dir=DIRECTORY
                              在 DIRECTORY 中搜索被包含的 makefile。
  -j [N], --jobs[=N]          同时允许 N 个任务;无参数表明允许无限个任务。
  -k, --keep-going            当某些目标无法创建时仍然继续。
  -l [N], --load-average[=N], --max-load[=N]
                              在系统负载高于 N 时不启动多任务。
  -L, --check-symlink-times   使用软链接及软链接目标中修改时间较晚的一个。
  -n, --just-print, --dry-run, --recon
                              只打印命令配方,不实际执行。
  -o FILE, --old-file=FILE, --assume-old=FILE
                              将 FILE 当做很旧,不必重新生成。
  -O[类型], --output-sync[=类型]
                           使用 <类型> 方式同步并行任务输出。
  -p, --print-data-base       打印 make 的内部数据库。
  -q, --question              不运行任何配方;退出状态说明是否已全部更新。
  -r, --no-builtin-rules      禁用内置隐含规则。
  -R, --no-builtin-variables   禁用内置变量设置。
  -s, --silent, --quiet       不输出配方命令。
  -S, --no-keep-going, --stop
                              关闭 -k。
  -t, --touch                 touch 目标而不是重新创建它们。
  --trace                     打印跟踪信息。
  -v, --version               打印 make 的版本号并退出。
  -w, --print-directory       打印当前目录。
  --no-print-directory        关闭 -w,即使 -w 默认开启。
  -W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE
                              将 FILE 当做最新。
  --warn-undefined-variables  当引用未定义变量的时候发出警告。

        -f        指定Makefile文件

        -n        只输出命令,但不执行,一般用于测试

        -s        只执行,不输出命令,可以在文件中使用@抑制命令输出

        -w        显示执行前后的路径

        -C        指定Makefile执行路径(通常需要改变执行路径的时候会使用该参数)

        -O        指定编译输出路径

        make工作流程

        1、读入所有的 Makefile。
        2、读入被 include 的其它 Makefile。
        3、初始化文件中的变量。
        4、推导隐晦规则,并分析所有规则。
        5、为所有的目标文件创建依赖关系链。
        6、根据依赖关系,决定哪些目标要重新生成。
        7、执行生成命令。

GCC编译流程

Usage: gcc [options] file...
Options:
  -pass-exit-codes         Exit with highest error code from a phase.
  --help                   Display this information.
  --target-help            Display target specific command line options.
  --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...].
                           Display specific types of command line options.
  (Use '-v --help' to display command line options of sub-processes).
  --version                Display compiler version information.
  -dumpspecs               Display all of the built in spec strings.
  -dumpversion             Display the version of the compiler.
  -dumpmachine             Display the compiler's target processor.
  -print-search-dirs       Display the directories in the compiler's search path.
  -print-libgcc-file-name  Display the name of the compiler's companion library.
  -print-file-name=<lib>   Display the full path to library <lib>.
  -print-prog-name=<prog>  Display the full path to compiler component <prog>.
  -print-multiarch         Display the target's normalized GNU triplet, used as
                           a component in the library path.
  -print-multi-directory   Display the root directory for versions of libgcc.
  -print-multi-lib         Display the mapping between command line options and
                           multiple library search directories.
  -print-multi-os-directory Display the relative path to OS libraries.
  -print-sysroot           Display the target libraries directory.
  -print-sysroot-headers-suffix Display the sysroot suffix used to find headers.
  -Wa,<options>            Pass comma-separated <options> on to the assembler.
  -Wp,<options>            Pass comma-separated <options> on to the preprocessor.
  -Wl,<options>            Pass comma-separated <options> on to the linker.
  -Xassembler <arg>        Pass <arg> on to the assembler.
  -Xpreprocessor <arg>     Pass <arg> on to the preprocessor.
  -Xlinker <arg>           Pass <arg> on to the linker.
  -save-temps              Do not delete intermediate files.
  -save-temps=<arg>        Do not delete intermediate files.
  -no-canonical-prefixes   Do not canonicalize paths when building relative
                           prefixes to other gcc components.
  -pipe                    Use pipes rather than intermediate files.
  -time                    Time the execution of each subprocess.
  -specs=<file>            Override built-in specs with the contents of <file>.
  -std=<standard>          Assume that the input sources are for <standard>.
  --sysroot=<directory>    Use <directory> as the root directory for headers
                           and libraries.
  -B <directory>           Add <directory> to the compiler's search paths.
  -v                       Display the programs invoked by the compiler.
  -###                     Like -v but options quoted and commands not executed.
  -E                       Preprocess only; do not compile, assemble or link.
  -S                       Compile only; do not assemble or link.
  -c                       Compile and assemble, but do not link.
  -o <file>                Place the output into <file>.
  -pie                     Create a position independent executable.
  -shared                  Create a shared library.
  -x <language>            Specify the language of the following input files.
                           Permissible languages include: c c++ assembler none
                           'none' means revert to the default behavior of
                           guessing the language based on the file's extension.

Options starting with -g, -f, -m, -O, -W, or --param are automatically
 passed on to the various sub-processes invoked by gcc.  In order to pass
 other options on to these processes the -W<letter> options must be used.

        gcc main.c        直接从源文件生成可执行文件

        过程:预处理->编译->汇编->链接        命令:gcc -E  ->  gcc -S  ->  gcc -c  ->  gcc xx.o

Makefile变量

        系统变量:

自动化变量描述
$@规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。
$%当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。
$<依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%” )定义的,那么“$<”就是符合模式的一系列的文件集合。
$?所有比目标新的依赖目标集合,以空格分开。
$^所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。
$+和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。
$*这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test。

        自定义变量:

                定义:OBJ = a.o b.o c.o        变量名 = 变量值

                使用:$(OBJ )                        $(变量名)/${变量名}

        赋值符:

                ' = '        使用最后一次有效值,变量名的值随着多次赋值而改变,由最后一次有效赋值决定。

                ' := '        使用当前语句赋值时的值,若后面再使用 ' := '赋值,则值更改。若定义变量希望使用当前值,则使用该赋值符。

                ' ?= '        判断变量是否已经被赋值,若已赋值,则使用该值,若未被赋值,则使用当前语句中被赋的值。

                ' += '        Makefile中的变量为字符串,当需要追加变量时,可以使用该赋值符。

模式规则

        模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符, a.%.c 就表示以 a.开头,以.c 结束的所有文件。当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方法如下:

    %.o : %.c
    命令

伪目标

        Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的,比如:

    clean:
    rm *.o
    rm main

        上述规则中并没有创建文件 clean 的命令,因此工作目录下永远都不会存在文件 clean,当我们输入“make clean”以后,后面的“rm *.o”和“rm main”总是会执行。可是如果我们“手贱”,在工作目录下创建一个名为“clean”的文件,那就不一样了,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会执行,我们预先设想的清理工程的功能也就无法完成。为了避免这个问题,我们可以将 clean 声明为伪目标,声明方式如下:

    .PHONY : clean

        我们使用伪目标来定义clean,输入“make clean”的话规则后面的 rm 命令都会执行。

条件判断

        在 C 语言中我们通过条件判断语句来根据不同的情况来执行不同的分支, Makefile 也支持条件判断,语法有两种如下:

    <条件关键字>
        <条件为真时执行的语句>
    endif

    以及:
    <条件关键字>
        <条件为真时执行的语句>
    else
        <条件为假时执行的语句>
    endif

        其中条件关键字有 4 个: ifeq、 ifneq、 ifdef 和 ifndef,这四个关键字其实分为两对、 ifeq 与ifneq、 ifdef 与 ifndef,先来看一下 ifeq 和 ifneq, ifeq 用来判断是否相等, ifneq 就是判断是否不相等, ifeq 用法如下:

    ifeq (<参数 1>, <参数 2>)
    ifeq ‘<参数 1 >’ ,‘ <参数 2>’
    ifeq “<参数 1>” , “<参数 2>”

        上述用法中都是用来比较“参数 1”和“参数 2”是否相同,如果相同则为真,“参数 1”和“参数 2”可以为函数返回值。 ifneq 的用法类似,只不过 ifneq 是用来比较“参数 1”和“参
数 2”是否不相等,如果不相等的话就为真。

    ifdef 和 ifndef 的用法如下:
    ifdef <变量名>

        如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。“变量名”同样可以是一个函数的返回值。 ifndef 用法类似,但是含义用户 ifdef 相反。

Shell命令调用

        在 Makefile 中调用shell 命令有两种形式。

        第一种是为了获取命令在shell环境中的执行结果。

        利用 $(shell commmand) 作为基本结构,不需要放在基本规则格式以制表符Tab开始的 command 位置处,我们提到的基本规则格式及文章开头展示的 目标-依赖-命令的形式。如下示例

    CUR_DIR := $(shell pwd)
    CUR_TIME := $(shell date)
    FILE_LIST := $(shell ls *.c)

        第二种是在单纯执行指定的命令以进行特定的操作。

        用在基本规则格式的 command 处,例如打印信息、创建文件夹、删除中间文件等等。

    all:
        mkdir build

    clean:
        rm hello_test build/*.o

         注意,这个函数会新生成一个 Shell 程序来执行命令,所以你要注意其运行性能,如果你的Makefile 中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有害的。特别是 Makefile 的隐晦的规则可能会让你的 shell 函数执行的次数比你想像的多得多。

循环

        Makefile中使用循环有两种方式,一是使用Makefile的函数foreach,二是使用shell for循环

    names = a b c d 
    files := $(foreach n,$(names),$(n).o)

        通过foreach遍历name中的值,并且存放在n中,使用$(n).o的匹配规则输出

        shell for:

    names = a b c d
    demo:
    	for x in $(names); do \
    	    echo $x; \
    	done

        在names中遍历,存放在x中,并使用echo打印,注意此时的for和echo后面需要增加 \ 这样视为一条命令。

函数

        函数调用

        Makefile 支持函数,类似 C 语言一样, Makefile 中的函数是已经定义好的,我们直接使用。 make 所支持的函数不多,但是绝对够我们使用了,函数的用法如下:

    $(函数名 参数集合)
    或者:
    ${函数名 参数集合}

        可以看出,调用函数和调用普通变量一样,使用符号“$”来标识。参数集合是函数的多个参数,参数之间以逗号“,”隔开,函数名和参数之间以“空格”分隔开,函数的调用以“$”开
头。接下来我们介绍《跟我一起写 Makefile》这份文档中记录的函数。

        字符串处理函数

        subst

    $(subst <from>,<to>,<text>)
    名称:字符串替换函数——subst。
    功能:把字串<text>中的<from>字符串替换成<to>。
    返回:函数返回被替换过后的字符串。

    示例:

    $(subst ee,EE,feet on the street),
    把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on thestrEEt”。

        patsubst

    $(patsubst <pattern>,<replacement>,<text>)
    名称:模式字符串替换函数——patsubst。
    功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符
    合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通
    配符“%”, 表示任意长度的字串。 如果<replacement>中也包含“%”, 那么, <replacement>
    中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“\”来转义, 以“\%”
    来表示真实含义的“%”字符)
    返回:函数返回被替换过后的字符串。

    示例:

    $(patsubst %.c,%.o,x.c.c bar.c)
    把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
    
    备注:
    这和我们前面“变量章节”说过的相关知识有点相似。
    如:
    “$(var:<pattern>=<replacement>)”
    相当于
    “$(patsubst <pattern>,<replacement>,$(var))”,
    而“$(var: <suffix>=<replacement>)” 则相当于
    “$(patsubst %<suffix>,%<replacement>,$(var))”。
    例如有:objects = foo.o bar.o baz.o,
    那么,“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。

        strip

    $(strip <string>)
    名称:去空格函数——strip。
    功能:去掉<string>字串中开头和结尾的空字符。
    返回:返回被去掉空格的字符串值。

    示例:
    $(strip a b c )
    把字串“a b c ”去到开头和结尾的空格,结果是“a b c”。

        findstring

    $(findstring <find>,<in>)
    名称:查找字符串函数——findstring。
    功能:在字串<in>中查找<find>字串。
    返回:如果找到,那么返回<find>,否则返回空字符串。

    示例:
    $(findstring a,a b c)
    $(findstring a,b c)
    第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)

        filter

    $(filter <pattern...>,<text>)
    名称:过滤函数——filter。
    功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以
    有多个模式。
    返回:返回符合模式<pattern>的字串。

    示例:
    sources := foo.c bar.c baz.s ugh.h
    foo: $(sources)
    cc $(filter %.c %.s,$(sources)) -o foo
    $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。

        filter-out

    $(filter-out <pattern...>,<text>)
    名称:反过滤函数——filter-out。
    功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以
    有多个模式。
    返回:返回不符合模式<pattern>的字串。

    示例:
    objects=main1.o foo.o main2.o bar.o
    mains=main1.o main2.o
    $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。

        sort

    $(sort <list>)
    名称:排序函数——sort。
    功能:给字符串<list>中的单词排序(升序)。
    返回:返回排序后的字符串。

    示例:$(sort foo bar lose)返回“bar foo lose” 。
    备注:sort 函数会去掉<list>中相同的单词。

        word

    $(word <n>,<text>)
    名称:取单词函数——word。
    功能:取字符串<text>中第<n>个单词。 (从一开始)
    返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返
    回空字符串。

    示例:$(word 2, foo bar baz)返回值是“bar”。

        wordlist

    $(wordlist <s>,<e>,<text>)
    名称:取单词串函数——wordlist。
    功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
    返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那
    么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单词
    串。

    示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。

        words

    $(words <text>)
    名称:单词个数统计函数——words。
    功能:统计<text>中字符串中的单词个数。
    返回:返回<text>中的单词数。

    示例:$(words, foo bar baz)返回值是“3”。
    备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。

        firstword

    $(firstword <text>)
    名称:首单词函数——firstword。
    功能:取字符串<text>中的第一个单词。
    返回:返回字符串<text>的第一个单词。

    示例:$(firstword foo bar)返回值是“foo”。
    备注:这个函数可以用 word 函数来实现:$(word 1,<text>)。

        以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里,举一个现实中应用的例子。我们知道,make 使用“VPATH”变量来指定“依赖文件”的搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数 CFLAGS,
        如:

     override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

        如 果 我 们 的 “$(VPATH)” 值 是 “src:../headers” , 那 么 “$(patsubst %,-I%,$(subst :, ,$(VPATH)))”将返回“-Isrc -I../headers”,这正是 cc 或 gcc 搜索头文件路径的参数。

        文件名操作函数

        下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是一系列的文件名来对待。

        dir

    $(dir <names...>)
    名称:取目录函数——dir。
    功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
    返回:返回文件名序列<names>的目录部分。

    示例: $(dir src/foo.c hacks)返回值是“src/ ./”。

        notdir

    $(notdir <names...>)
    名称:取文件函数——notdir。
    功能:从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“ /”)之后的部分。
    返回:返回文件名序列<names>的非目录部分。

    示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。

        suffix

    $(suffix <names...>)
    名称:取后缀函数——suffix。
    功能:从文件名序列<names>中取出各个文件名的后缀。
    返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。

    示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。

        basename

    $(basename <names...>)
    名称:取前缀函数——basename。
    功能:从文件名序列<names>中取出各个文件名的前缀部分。
    返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。

    示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。

        addsuffix

    $(addsuffix <suffix>,<names...>)
    名称:加后缀函数——addsuffix。
    功能:把后缀<suffix>加到<names>中的每个单词后面。
    返回:返回加过后缀的文件名序列。

    示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。

        addprefix

    $(addprefix <prefix>,<names...>)
    名称:加前缀函数——addprefix。
    功能:把前缀<prefix>加到<names>中的每个单词前面。
    返回:返回加过前缀的文件名序列。
    示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。

        join

    $(join <list1>,<list2>)
    名称:连接函数——join。
    功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。
    如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。
    返回:返回连接过后的字符串。

    示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。

        foreach 函数

        foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的, Makefile中的 foreach 函数几乎是仿照于 Unix 标准 Shell( /bin/sh)中的 for 语句,或是 C-Shell(/bin/csh)中的 foreach 语句而构建的。它的语法是:

    $(foreach <var>,<list>,<text>)

       这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值。所以, <var>最好是一个变量名, <list>可以是一个表达式,而<text>中一般会使用<var>这个参数来依次枚举<list>中的单词。举个例子:

    names := a b c d
    files := $(foreach n,$(names),$(n).o)

        上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为 foreach 函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。注意,foreach 中的<var>参数是一个临时的局部变量,foreach 函数执行完后,参数<var>的变量将不在作用,其作用域只在 foreach 函数当中。

        if 函数

        if 函数很像 GNU 的 make 所支持的条件语句——ifeq(参见前面所述的章节),if 函数的语法是:

    $(if <condition>,<then-part>)
    或是
    $(if <condition>,<then-part>,<else-part>)

        可见,if 函数可以包含“else”部分,或是不含。即 if 函数的参数可以是两个,也可以是三个。<condition>参数是 if 的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,<then-part>会被计算,否则<else-part>会被计算。而 if 函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。所以,<then-part>和<else-part>只会有一个被计算。

        call 函数

        call 函数是唯一 一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用 call 函数来向这个表达式传递参数。其语法是:

    $(call <expression>,<parm1>,<parm2>,<parm3>...)

        当 make 执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是 call 函数的返回值。例如:

    reverse = $(1) $(2)
    foo = $(call reverse,a,b)

        那么,foo 的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如:

    reverse = $(2) $(1)
    foo = $(call reverse,a,b)

        此时的 foo 的值就是“b a”。

        origin 函数

        origin 函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是:

    $(origin <variable>)

        注意,<variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用“$”字符。Origin 函数会以其返回值来告诉你这个变量的“出生情况”,下面,是 origin函数的返回值:

        “undefined”        如果<variable>从来没有定义过,origin 函数返回这个值“undefined”。
        “default”             如果<variable>是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。environment”  如果<variable>是一个环境变量,并且当 Makefile 被执行时, “-e”参数没有被打开。
        “file”                   如果<variable>这个变量被定义在 Makefile 中。
        “command line”  如果<variable>这个变量是被命令行定义的。
        “override”           如果<variable>是被 override 指示符重新定义的。
        “automatic”        如果<variable>是一个命令运行中的自动化变量。

        这些信息对于我们编写 Makefile 是非常有用的,例如,假设我们有一个 Makefile 其包了一个定义文件 Make.def,在 Make.def 中定义了一个变量“bletch”,而我们的环境中也有一个环境变量“bletch”,此时,我们想判断一下,如果变量来源于环境,那么我们就把之重定义了,如果来源于 Make.def 或是命令行等非环境的,那么我们就不重新定义它。于是,在我们的 Makefile 中,我们可以这样写:

    ifdef bletch
        ifeq "$(origin bletch)" "environment"
            bletch = barf, gag, etc.
        endif
    endif

        控制 make 的函数

        make 提供了一些函数来控制 make 的运行。通常,你需要检测一些运行 Makefile 时的运行时信息,并且根据这些信息来决定,你是让 make 继续执行,还是停止。

        error

    $(error <text ...>)
    产生一个致命的错误,<text ...>是错误信息。注意,error 函数不会在一被使用就会
    产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么
    也是可以的。例如:

    示例一:
    ifdef ERROR_001
        $(error error is $(ERROR_001))
    endif

    示例二:
    ERR = $(error found an error!)
    .PHONY: err
    err: ; $(ERR)

    示例一会在变量 ERROR_001 定义了后执行时产生 error 调用,而示例二则在目录 err被执行时才发生 error 调用。

        warning

    $(warning <text ...>)
    这个函数很像 error 函数,只是它并不会让 make 退出,只是输出一段警告信息,而 make 继续执行。

        自定义函数

    .PHONY : test

    define foo1
        @echo "My name is $(0)"
    endef

    define foo2
        @echo "my name is $(0)"
        @echo "param => $(1)"
    endef

    var := $(call foo1)    
    new := $(foo1)         

    test :
        @echo "var => $(var)"   
        @echo "new => $(new)"   
        $(call foo1) 
        $(call foo2, Jiau)

        输出结果:

    var =>  @echo My name is foo1
    new =>  @echo My name is 
    My name is foo1
    my name is foo2
    param =>  Jiau

引用其他Makefile

        在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,这很像 C 语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include 的语法是:

    include <filename>

        filename 可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符) 在 include前面可以有一些空字符,但是绝不能是[Tab]键开始。include 和<filename>可以用一个或多个空格隔开。举个例子,你有这样几个 Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了 e.mk 和 f.mk,那么,下面的语句:

    include foo.make *.mk $(bar)
    等价于:
    include foo.make a.mk b.mk c.mk e.mk f.mk

        make 命令开始时,会把找寻 include 所指出的其它 Makefile,并把其内容安置在当前的位。就好像 C/C++的#include 指令一样。如果文件都没有指定绝对路径或是相对路径的话,make 会在当前目录下首先寻找,如果当前目录下没有找到,那么,make 还会在下面的几个目录下找:
        1、如果 make 执行时,有“-I”或“--include-dir”参数,那么 make 就会在这个参数所指定的目录下去寻找。
        2、如果目录<prefix>/include(一般是:/usr/local/bin 或/usr/include)存在的话,make 也会去找。如果有文件没有找到的话,make 会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找到,或是不能读取的文件,如果还是不行,make 才会出现一条致命信息。如果你想让 make不理那些无法读取的文件,而继续执行,你可以在 include 前加一个减号“-”。

    如: -include <filename>

        其表示,无论 include 过程中出现什么错误,都不要报错继续执行。和其它版本 make兼 容的相关命令是 sinclude,其作用和这一个是一样的。

文件搜寻

        在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。

        Makefile 文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

    VPATH = src:../headers

        上面的的定义指定两个目录,“src”和“../headers”,make 会按照这个顺序进行搜索。目录由“冒号”分隔。 (当然,当前目录永远是最高优先搜索的地方)另一个设置文件搜索路径的方法是使用 make 的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个 make 的关键字,这和上面提到的那个 VPATH 变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:

    1、vpath <pattern> <directories>
    为符合模式<pattern>的文件指定搜索目录<directories>。

    2、vpath <pattern>
    清除符合模式<pattern>的文件的搜索目录。

    3、vpath
    清除所有已被设置好了的文件搜索目录。

        vapth 使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录。例如:

    vpath %.h ../headers

        该语句表示,要求 make 在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)

我们可以连续地使用 vpath 语句,以指定不同搜索策略。如果连续的 vpath 语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make 会按照 vpath 语句的先后顺序来执行搜索。如:

    vpath %.c foo
    vpath %.c blish
    vpath %.c bar

        其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。
 

Makefile还包含一些其他知识点,比如隐含规则,动态、静态库的链接与使用,这些知识等使用到的时候再进行记录!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absorbed_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值