Makefile 入门

一:基本概念

  • 目标(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)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章