Makefile基本入门法

  • Post author:
  • Post category:其他


一:基本概念


  • 目标(target):

    位于‘:’的前面,其名字可以是由字母和下划线‘_’组成。

  • 假目标(phony target)

    :用于解决所定义的目标与所存在的文件是同名的的问题。实例:.PHONY: clean

  • 先决条件(prerequisites)

    :生成某个目标时,所依赖的目标。通常位于’:’后面。

  • 命令(commands)

    :通常位于目标下面。每条规则中的命令和操作系统 Shell 的命令行是一致的。make 会按顺序一条一条地执行命令,

    每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的

    。在命令行之间中的空格或是空行会被忽略,但是,如果该空格或空行是以 Tab 键开头的,那么 make 会认为其是一个空命令。


  • 规则


    • 由目标(targets)、先决条件(prerequisites)以及命令(commands)所组成的。
    • Makefile的基本单元。




目标和先决条件之间表达的就是依赖关系(dependency),这种依赖关系指明在构建目标之前,必须保证先决条件先满足(或构建)。而先决条件可以是其它的目标,当先决条件是目标时,其必须先被构建出来。 还有就是一个规则中目标可以有多个,当存在多个目标,且这一规则是 Makefile 中的第一个规则时,如果我们运行 make 命令不带任何目标,那么规则中的第一个目标将被视为是缺省目标。

这里写图片描述

  • 实例
源文件:
//foo.c
#include <stdio.h>
void foo ()
{
    printf (“This is foo ()!\n”);
}

//main.c
extern void foo ();
int main ()
{
    foo ();
    return 0;
}


makefile初版1
#-c:表示只编译不链接。
all: main.o foo.o
    gcc -o simple main.o foo.o
main.o: main.c
    gcc -o main.o -c main.c
foo.o: foo.c
    gcc -o foo.o -c foo.c
clean:
    rm simple.exe main.o foo.o

二:变量


  • 概念

    :一个变量的定义很简单,就是一个名字(变量名)后面跟上一个等号,然后在等号的后面放这个变量所期望的值。对            于变量的引用,则需要采用

    $(变量名)

    或者

    ${变量名}

    这种模式。

  • 变量的分类


1. 自动变量

:用于解决目标和先决条件的名字会在规则的命令中多次出现的问题。

$@:用于表示一个规则中的目标。当我们的一个规则中有多个目标时, $@所指的是其中任何造成命令被运行的目标。

$^:则表示的是规则中的所有先择条件。

$<:表示的是规则中的第一个先决条件。


2. 特殊变量

:make程序定义好的变量。

CC = cc #c语言编译器的名称
CPP = $(cc) -E #c文件预处理器的名称
CFLAGS #C文件的编译选项
CPPFLAGS #C文件预处理的编译选项
CXXFLAGS #CPP文件的编译选项
LDFLAGS #连接的动态库
 
CURDIR := /home/zxy/... #当前路径
MAKEFLAGS = p #make命令选项
RM = rm -f
VPATH #文件的搜索路径

MAKE:指make命令名是什么。当我们需要在 Makefile 中调用另一个 Makefile 时需要用到这个变量, 采用这种方式,有利于写一个容易移植的 Makefile。
MAKECMDGOALS: 指的是用户输入的目标。

  • 变量的类别

    (赋值)


1. 递归扩展变量(recursively expanded variable)

:使用“=”操作符进行变量定义和赋值,递归扩展变量的引用是递归的。                 其值为推导到最后一次的赋值值。


2. 简单扩展变量(simply expanded variables)

:使用“:=”操作符来定义的。 对于这种变量, make 只对其进行一次扫描和                  替换。

3.

条件赋值符

:使用“?=”操作符来定义的。条件赋值的意思是当变量以前没有定义时,就定义它并且将左边的值赋值给

它,如果已经定义了那么就不再改变其值。条件赋值类似于提供了给变量赋缺省值的功能。

4.

追加赋值

:使用“+=”操作符来定义的。原变量用追加一个新值。

5.

多行变量

define two-lines
echo foo
echo $(bar)
endef


  • 变量及其值的来源

1 . 对于前面所说到的自动变量,其值是在每一个规则中根据规则的上下文自动获得变量值的。

2 .可以在运行 make 时,在 make 命令行上定义一个或多个变量。如果采用 make bar=x 运行 Makefile,则得到的结果将完               全不同。在 make 命令行中定义的变量及其值同样在 Makefile 中是可见的。其实,我们可以通过在 make 命令行中定义              变量的方式从而覆盖 Makefile 中所定义的变量的值。

3 .变量还可以来自于 Shell 环境, 如,采用 Shell 中的 export 命令定义了一个 bar变量。


  • 高级变量引用功能

在斌值的同时完成后缀替换操作。如:

PHONY: all
foo = a.o b.o c.o
bar := $(foo:.o=.c)
all:
    @echo "bar = $(bar)"


  • override 指令

我们可以采用在 make 命令行上定义变量的方式, 使得 Makefile 中定义的变量被覆盖掉,从而不起作用。 可能,在设计             Makefile 时,我们并不希望用户将我们在 Makefile 中定义的某个变量覆盖掉,那就得用 override 指令了。

#更新makefile2:

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o

$(EXE): $(OBJS)
    $(CC) -o $@ $^
main.o: main.c
    $(CC) -o $@ -c $^
foo.o: foo.c
    $(CC) -o $@ -c $^
clean:
    $(RM) $(EXE) $(OBJS)

三:模式

  • 解决问题:

如果对于每一个目标文件都得写一个不同的规则来描述,那会是一种“体力活”,太繁了!对于一个大型项目, 就更不用说了。 Makefile 中的模式就是用来解决我们的这种烦恼的。

# 更新makefile3:

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o

$(EXE): $(OBJS)
    $(CC) -o $@ $^
    
#模式应用
%.o: %.c
    $(CC) -o $@ -c $^
    
clean:
    $(RM) $(EXE) $(OBJS)

四:条件判断

使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。

注意:


make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。

条件表达式的语法为:
    # 111    
    <conditional-directive>
    <text-if-true>
    endif

    # 222
    <conditional-directive>
    <text-if-true>
    else
    <text-if-false>
    endif

    # 333
    ifeq (<arg1>, <arg2>)
    <text-if-true>
    else ifeq (<arg3>, <arg4>)
    <text-if-true>
    else
    <text-if-true>
    endif

其中<conditional-directive>表示条件关键字,如“ifeq”。这个关键字有四个。
(1)第一个是“ifeq”:
    注:ifeq 后面参数要加$(), 因为是值引用, 值可以为数值或字符串
    ifeq (<arg1>, <arg2> ) 
比较参数“arg1”和“arg2”的值是否相同。当然,参数中还可以使用make的函数。如:
    ifeq ($(strip $(foo)),)
    <text-if-empty>
    endif
这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>就生效。

(2)第二个条件关键字是“ifneq”。语法是:
    ifneq (<arg1>, <arg2> ) 
其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。

(3)第三个条件关键字是“ifdef”。语法是:
     ifdef <variable-name> 
    如果变量<variable-name>是否有值,那表达式为真。否则,表达式为假。当然,<variable-name>同
样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子:
    注:ifdef后的变量名不能加 $()。
示例一:
    bar =
    foo = $(bar)
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif
示例二:
    foo =
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif
第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。

(4)第四个条件关键字是“ifndef”。其语法是:
     ifndef <variable-name>
 和“ifdef”是相反的意思。
     在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然
就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。



feq ($(TARGET_ARCH), arm)
        LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), x86)
        LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), mips)
        LOCAL_SRC_FILES := ...
else 
        LOCAL_SRC_FILES := ...
endif


五:函数


  • addprefix 函数

addprefix 函数是用来在给字符串中的每个子串前加上一个前缀,其形式是:

$(addprefix prefix, names…)

实例代码:

.PHONY: all
without_dir = foo.c bar.c main.o
with_dir := $( addprefix objs/, $(without_dir))
all:
    @echo $(with_dir)


  • filter函数

filter 函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:

$(filter pattern…, text)

实例代码:

.PHONY: all
sources = foo.c bar.c baz.s ugh.h
sources := $(filter %.c %.s, $(sources))
all:
    @echo $(sources)

  • filter-out函数

filter-out 函数用于从一个字符串中根据模式滤除一部分字符串,filter 与 filter-out 是互补的。其形式是:

$(filter-out pattern…, text)

实例代码:

.PHONY: all
objects = main1.o foo.o main2.o bar.o
result = $(filter-out main%.o, $(objects))
all:
    @echo $(result)


  • patsubst函数

patsubst 函数是用来进行字符串替换的,其形式是:

$(patsubst pattern, replacement, text)

实例代码:

.PHONY: all
mixed = foo.c bar.c main.o
objects := $(patsubst %.c, %.o, $(mixed))
all:
    @echo $(objects)

  • strip函数

strip 函数用于去除变量中的多余的空格,其形式是:

$(strip string)

实例代码:

.PHONY: all
original = foo.c bar.c
stripped := $(strip $(original))
all:
    @echo "original = $(original)"
    @echo "stripped = $(stripped)"

  • wildcard 函数

wildcard 是通配符函数,通过它可以得到我们所需的文件,这个函数如果我们在 Windows 或是Linux 命令行中的“*”。 其形式是:

$(wildcard pattern)

实例代码:

.PHONY: all
SRCS = $(wildcard *.c)
all:
    @echo $(SRCS)
# 更新makefile4 

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE): $(OBJS)
    $(CC) -o $@ $^
%.o: %.c
    $(CC) -o $@ -c $^
clean:
    $(RM) $(EXE) $(OBJS)



版权声明:本文为vertor11原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。