• Makefile基础知识


    注:本文是《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6.pdf》一书学习的笔记,大部分内容为书籍中的内容。

    GCC编译常用参数

    GCC命令格式如下:

    gcc [选项] [文件名字]
    

    常用的一些编译选项如下:

    -c:只编译不链接为可执行文件,编译器将输入的.c 文件编译为.o 的目标文件。

    -o:<输出文件名>用来指定编译结束以后的输出文件名,如果不使用这个选项的话 GCC 默认编译出来的可执行文件名字为 a.out。

    -g:添加调试信息,如果要使用调试工具(如 GDB)的话就必须加入此选项,此选项指示编译的时候生成调试所需的符号信息。

    -O:对程序进行优化编译,如果使用此选项的话整个源代码在编译、链接的的时候都会进行优化,这样产生的可执行文件执行效率就高。

    -O2:比-O 更幅度更大的优化,生成的可执行效率更高,但是整个编译过程会很慢。

    GCC编译编译流程

    GCC编译器的编译流程是:预处理、编译、汇编和链接

    预处理:就是展开所有的头文件、替换程序中的宏、解析条件编译并添加到文件中。

    编译:是将经过预编译处理的代码编译成汇编代码,也就是我们常说的程序编译。

    汇编:就是将汇编语言文件编译成二进制目标文件。

    链接:就是将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉及到静态库和动态库等问题。

    Makefile规格格式

    Makefile由一系列规则组成,规则的格式如下:

    目标...: 依赖文件集合...
    	命令1
    	命令2
    	...
    

    比如:

    main : main.o input.o calcu.o
    	gcc -o main main.o input.o calcu.o
    

    规则的目标是main,生成main依赖于main.o input.o calcu.o。

    Makefile变量

    Makefile中的变量都是字符串。

    objects = main.o input.o calcu.o  # 定义一个objects变量
    main : main.o input.o calcu.o
    	gcc -o main $(objects)
    

    Makefile变量的赋值符:

    赋值符“=”

    objects = main.o input.o calcu.o  # 定义一个objects变量
    print:  
    	@echo $(objects)  # “@”不输出命令执行过程
    
    $ make print
    main.o input.o calcu.o
    

    赋值符“:=”

    ":="赋值符使用的是变量前一次的定义。

    name = linux
    curname := $(name)
    name = Linux4_10
    
    print:
        @echo $(curname)
    

    赋值符“?=”

    name ?= linux
    name ?= Linux4_10
    
    print:
        @echo $(name)
    
    $ make print
    linux
    

    如果变量name前面没有被赋值,那么此变量就是“Linux4_10”,如果前面已经赋过值了,那么就使用前面赋的值。

    变量追加“+=”

    给已经定义好的变量添加一些字符串进去,就要使用到符号“+=”。

    name = linux
    name += 4_10
    
    print:
        @echo $(name)
    
    $ make print   
    linux 4_10
    

    Makefile模式规则

    在编译.c源文件为.o源文件时,如果每一个C文件都写一个对应的规则,则文件增多的时候维护就很复杂,这时候就可以使用模式规则。

    模式规则中,在规则的目标定义中要包含”%“,目标中的”%“表示对文件名的匹配,”%“表示长度任意的非空字符串,比如"%.c"就是所有的以.c结尾的字符串。当”%“出现在目标中的时候,目标中的”%“代表的值决定了依赖中的”%“值,用法如下:

    %.o:%.c
    	命令
    

    比如:

    objects = main.o input.o calcu.o
    main: $(objects)
    	gcc -o main $(objects)
    	
    %.o:%.c
    	命令 # 命令的实现需要借助自动化变量,见下一章节
    
    clean:
    	rm *.o
    	rm main
    

    Makefile自动化变量

    自动化变量:把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中。常见的自动化变量如下:

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

    7 个自动化变量中,常用的三种:$@、$<和$^。

    增加自动变量的Makefile如下:

    objects = main.o input.o calcu.o
    main: $(objects)
    	gcc -o main $(objects)
    	
    %.o:%.c
    	gcc -c $<
    
    clean:
    	rm *.o
    	rm main
    
    $ make
    gcc -c main.c
    gcc -c input.c
    gcc -c calcu.c
    gcc -o main main.o input.o calcu.o
    

    Makefile伪目标

    伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。

    使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件。

    比如:清理工程的代码

    clean:
    	rm *.o
    	rm main
    

    当我们输入“make clean”以后,后面的“rm *.o”和“rm main”总是会执行。如果当前目录下有一个名为“clean”的文件,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会执行,我们预先设想的清理工程的功能也就无法完成。

    $ touch clean
    $ make clean
    make: `clean' is up to date.
    

    伪目标可以解决上述的问题,伪目标声明方式如下:

    .PHONY:clean
    
    clean:
        rm *.o
        rm main
    

    Makefile条件判断

    Makefile支持条件判断,语法有两种,如下:

    <条件关键字>
    	<条件为真时执行的语句>
    
    <条件关键字>
    	<条件为真时执行的语句>
    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>"
    ifeq "<参数 1>", '<参数 2>'
    ifeq '<参数 1>', "<参数 2>"
    
    strip = need
    foo = need
    
    ifeq ($(strip), $(foo))
        ret = yes
    endif
    
    print:
        @echo $(ret)
    
    $ make print   
    yes
    

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

    ifdef 和 ifndef 的用法如下:

    ifdef <变量名>
    

    如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。“变量名”同样可以是一个函数的返回值。

    bar =
    foo = $(bar)
    ifdef foo 
        frobozz = yes
    else
        frobozz = no
    endif
    
    print:
        @echo $(frobozz)
    
    $ make print
    yes
    

    Makefile函数使用

    Makefile 中的函数是已经定义好的,我们直接使用,不支持我们自定义函数。

    函数的用法如下:

    $(函数名 参数集合)
    

    或者:

    ${函数名 参数集合}
    

    调用函数和调用普通变量一样,使用符号“$”来标识。参数集合是函数的多个参数,参数之间以逗号“,”隔开,函数名和参数之间以“空格”分隔开,函数的调用以“$”开头。

    常用函数:

    函数subst

    函数subst用来完成字符串替换,调用形式如下:

    $(subst <from>,<to>,<text>)
    

    此函数的功能是将字符串<text>中的<from>内容替换为<to>,函数返回被替换以后的字符串。

    print:
        @echo $(subst zzq,ZZQ,my name is zzq)
    
    $ make print   
    my name is ZZQ
    

    函数patsubst

    函数 patsubst 用来完成模式字符串替换,使用方法如下:

    $(patsubst <pattern>,<replacement>,<text>)
    

    此函数查找字符串<text>中的单词是否符合模式<pattern>,如果匹配就用<replacement>来替换掉,<pattern>可以使用通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字符串。如果<replacement>中也包涵“%”,那么<replacement>中的“%”将是<pattern>中的那个“%”所代表的字符串。

    print:
        @echo $(patsubst %.c, %.o, a.c b.c c.c)
    
    $ make print   
    a.o b.o c.o
    

    函数dir

    函数 dir 用来获取目录,使用方法如下:

    $(dir <names...>)
    

    此函数用来从文件名序列<names>中提取出目录部分,返回值是文件名序列<names>的目录部分。

    print:
        @echo $(dir src/a.c)
    
    $ make print   
    src/
    

    函数notdir

    去除文件中的目录部分,也就是提取文件名,用法如下:

    $(notdir <names...>)
    

    此函数用与从文件名序列<names>中提取出文件名非目录部分。

    print:
        @echo $(notdir src/a.c)
    
    $ make print   
    a.c
    

    函数foreach

    foreach 函数用来完成循环,用法如下:

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

    把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,<text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。

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

    函数wildcard

    通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard,使用方法如下:

    $(wildcard PATTERN...)
    

    比如:

    print:
        @echo $(wildcard *.c)  # 获取当前目录下的所有C文件
    
    $ make print   
    calcu.c main.c input.c
    
  • 相关阅读:
    git 提交解决冲突(转载)
    impala系列: 时间函数
    impala系列: 字符串函数
    Impala系列: Impala常用的功能函数
    impala系列:impala特有的操作符
    impala系列: 同步Hive元数据和收集统计信息
    ETL脚本的版本管理方法和 SourceTree 使用
    几本不错的数据仓库和Hadoop书籍
    Kudu系列-基础
    sql parser
  • 原文地址:https://www.cnblogs.com/mrlayfolk/p/16127009.html
Copyright © 2020-2023  润新知