makefile实例

Makefile的介绍

使用 GCC 的命令行进行程序编译在单个文件下是比较方便的,当工程中的文件逐渐增多,甚至变得十分庞大的时候,使用 GCC 命令编译就会变得力不从心。Linux 中的 make 工具提供了一种管理工程的功能,可以方便的进行程序的编译,对更新的文件进行重编译。

Makefile的基本格式为:

TARGET... : DEPENDEDS...
    COMMAND
    ...
    ...
  • TARGET:规则所定义的目标,通常是最后生成的文件,也可以是一个“动作”,称之为“伪目标”。
  • DEPENDEDS:执行此规则所必须的依赖条件。
  • COMMAND:规则所执行的命令。命令可以是多个,每个命令占一行,以 Tab 开头。

使用make进行项目管理,需要编写Makefile。在编译时,make程序按照顺序从Makefile文件中读取指令,依次执行!

当需要编译工程时,直接在工程目录中执行make即可。如果想清除编译过程中生成的目标文件,执行make clean即可。

makefile两种执行方式:直接make与make -f 指定文件
如果在本地工程目录下有文件名为makefile的makefile文件,使用make执行编译,使用make clean执行清除目标文件。
如果在本地工程目录下有指定的makefile文件,比如:demo.makefile文件,使用make -f demo.makefile执行编译,使用make clean -f demo.makefile执行清除目标文件。

使用预定义变量的Makefile

在Makefile中还有一些变量是系统预定义的,用户可以直接使用。

Makefile中经常使用的变量及含义

变量名含 义默 认 值
AR生成静态库库文件的程序名称ar
AS汇编编译器的名称as
CCC语言编译器的名称cc
CPPC语言预编译器的名称$(CC) -E
CXXC++语言编译器的名称g++
FCFORTRAN语言编译器的名称f77
RM删除文件程序的名称rm -f
ARFLAGS生成静态库库文件程序的选项无默认值
ASFLAGS汇编语言编译器的编译选项无默认值
CFLAGSC语言编译器的编译选项无默认值
CPPFLAGSC语言预编译器的编译选项无默认值
CXXFLAGSC++语言编译器的编译选项无默认值
FFLAGSFORTRAN语言编译器的编译选项无默认值

因此,前面的Makefile文件可以改写成:

CFLAGS = -Iadd -Isub -O2
OBJS = add/add_int.o add/add_float.o \
       sub/sub_int.o sub/sub_float.o main.o
TARGET = cacu

$(TARGET):$(OBJS)
    $(CC) -o $(TARGET) $(OBJS) $(CFLAGS)

clean:
    -$(RM) $(TARGET) $(OBJS)

其中变量$(CC) $(RM)可以直接使用,默认值分别是ccrm -f。另外CFLAGS等变量是调用编译器时的默认选项配置,在生成main.o时没有指定编译选项,make程序自动调用了文件中定义的CFLAGS选项来增加头文件的搜索路径。

使用自动变量的Makefile

还记得上面出现的 <和<和@ 吗?它们是Makefile中的自动变量,分别代表依赖项和目标项。下面是一些常见的自动变量及其含义:

Makefile 中常见的自动变量和含义

变量含义
*表示目标文件的名称,不包含目标文件的扩展名
+表示所有的依赖文件,这些依赖文件之间以空格分开,按照出现的先后为顺序,其中可能包含重复的依赖文件
<表示依赖项中第一个依赖文件的名称
?依赖项中,所有目标文件时间戳晚的依赖文件,依赖文件之间以空格分开
@目标项中目标文件的名称
^依赖项中,所有不重复的依赖文件,这些文件之间以空格分开

由此,对上面的Makefile文件进行重写,代码如下:

CFLAGS = -Iadd -Isub -O2
OBJS = add/add_int.o add/add_float.o \
       sub/sub_int.o sub/sub_float.o main.o
TARGET = cacu

$(TARGET):$(OBJS)
    $(CC) $^ -o $@ $(CFLAGS)

$(OBJS):%o:%c
    $(CC) -c $< -o $@ $(CFLAGS)

clean:
    -$(RM) $(TARGET) $(OBJS)

Makefile的实例

实例

add.h

#include <stdio.h>

int add(int a,int b);

add.cpp

#include "add.h"

int add(int a,int b)
{
	int r = 0;
	r = a+b;
	return r;
}

main.cpp

#include "add.h"

int main(int argc, char *argv[])
{
	printf("simple demo \n");
	printf("add(91,8)=%d\n",add(91,8));
	
	return 0;
}

main.makefile文件或makefile文件

test:main.o add.o
	g++ main.o add.o -o test
	
main.o:main.cpp add.h
	g++ -c main.cpp -o main.o
add.o:add.cpp add.h
	g++ -c add.cpp -o add.o

.PHONY:clean
clean:
	rm -rf *.o test

文件名为makefile的makefile文件,使用make执行编译,使用make clean执行清除目标文件。
文件名为main.makefile文件,使用make -f main.makefile执行编译,使用make clean -f main.makefile执行清除目标文件。

简化后的makefile文件

#这是简化后的makefile
CC=g++
OBJS=main.o add.o

test:$(OBJS)
	${CC} $^ -o $@
%.o:%.c
	${CC} -c $< -o $@
    
clean:
	rm -rf *.o test

执行makefile并编译,生成test文件。

最后执行文件,输入命令./test

结果输出:

simple demo
add(91,8)=99
 

makefile中的$@, $^, $< , $?, $%, $+, $*

$@  表示目标文件
$^  表示所有的依赖文件
$<  表示第一个依赖文件
$?  表示比目标还要新的依赖文件列表
$%    表示仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是“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所不能识别的,那么“$*”就是空值。

Makefile中的条件判断
Makefile 中有多个条件判断语句,可以根据不同条件执行不同的命令。
ifeq:if equal的缩写,判断是否相等,相等返回true,不相等返回false
ifneq:if not equal的缩写,判断是否不相等,不相等返回true,相等返回false
ifdef:if definite的缩写,判断变量是否存在,存在返回true,不存在返回false
ifndef:if not definite的缩写,判断变量是否不存在,不存在返回true,存在返回false
else:否则(可有可无)
endif:结束if判断(一定要写)

以下是常见的 Makefile 条件判断语句:

1.ifeq 和 ifneq
ifeq 和 ifneq 分别表示等于和不等于的条件判断语句,用法如下:

ifeq ($(VARIABLE),value)
    ...
else
    ...
endif

ifneq ($(VARIABLE),value)
    ...
else
    ...
endif
其中,$(VARIABLE) 是需要被判断的变量名,value是需要和变量比较的值。如果判断是正确的,则执行第一组命令;否则执行第二组命令。

2.ifdef 和 ifndef
ifdef 和 ifndef 用于判断变量是否被定义,用法如下:

ifdef VARIABLE
    ...
else
    ...
endif

ifndef VARIABLE
    ...
else
    ...
endif
如果变量 VARIABLE 被定义,则执行第一组命令;否则执行第二组命令。

3.ifeq 的比较操作符
ifeq 还支持比较操作符,如 >、<、>=、<=、!= 等。例如:

ifeq ($(NUM1), $(NUM2))
    ...
endif

ifeq ($(NUM1), 10)
    ...
else ifeq ($(NUM1), 20)
    ...
else
    ...
endif

ifeq ($(strip $(SOME_VAR)),)
    ...
endif
这里用到了比较两个变量的值,根据不同条件执行不同命令。第三个例子中,使用了 strip 函数,去掉变量值中的所有空格,并且判断是否为空。

Makefile中的循环
makefile中支持循环的,并且循环有两种:
GNU平台下Makefile有默认的foreach循环
其他平台可以调用shell脚本的循环
调用shell指令可以通过$(shell 指令)或者指令的形式都可以
示例:循环创建文件和文件夹

target:=a b c d
filename=$(foreach v, $(target), $v.txt)

show:
	@echo $(target)
	@echo $(foreach v, $(target), $v)
	for v in $(target);\
		do touch $$v.txt;\
	done
	$(shell for v in $(target); do mkdir $$v-txt;done)

clean:
	${RM} -rf $(target) $(filename) *txt

Makefile中定义命令包
命令包有点像是个函数, 将连续的相同的命令合成一条, 减少 Makefile 中的代码量, 便于以后维护。
语法:
define <command-name>
command
...
endef

示例:

define run_demo_makefile
@echo -n "Hello"
@echo " Makefile!"
@echo "这里可以执行多条 Shell 命令!"
endef

all:
	$(run_demo_makefile)

Makefile中使用shell 命令
在 Makefile 中调用shell 命令有两种形式。
1.1 第一种是为了获取命令在shell环境中的执行结果。
利用 $(shell commmand) 作为基本结构,不需要放在基本规则格式以制表符Tab开始的 command 位置处,我们提到的基本规则格式及文章开头展示的 target-prerequisted-command 的形式。如下示例
CUR_DIR := $(shell pwd)
CUR_TIME := $(shell date)
FILE_LIST := $(shell ls *.c)

all:
@echo $(CUR_DIR)
@echo $(CUR_TIME)
@echo $(FILE_LIST)

#@echo回显内容

1.2 第二种是在单纯执行指定的命令以进行特定的操作。
用在基本规则格式的 command 处,例如打印信息、创建文件夹、删除中间文件等等。
all:
mkdir build
clean:
rm hello_test build/*.o

Makefile常用函数
Makefile常用字符串函数
把符合某一格式的字符串进行替换 patsubst
字符串替换函数 subst
整理空格函数 strip
查找字符串函数 findstring
过滤函数filter 和 反过滤函数filter-out
排序函数 sort
取单词函数 word
Makefile常用文件操作函数
取目录路径函数 dir
取文件名 函数 notdir
取后缀名 函数 suffix
取前缀 函数 notdir
添加后缀名函数 addsuffix
添加前缀名函数 addperfix
链接函数 join:拼接
获取匹配模式文件名函数 wildcard
Makefile中的其它常用函数
foreach
if
call 创建新的参数化的函数
origin 寻找变量的来源                   
详细参考:https://blog.csdn.net/akuaner/article/details/119534310

查看目标的依赖关系

写 Makefile 的时候, 需要确定每个目标的依赖关系。GNU提供一个机制可以查看C代码文件依赖那些文件。
查看C文件的依赖关系
gcc -MM main.c
结果输出
main.o: main.cpp add.h
查看C++文件的依赖关系
g++ -MM main.cpp
结果输出
main.o: main.cpp add.h

通用Makefile编写,参考下面文章
通用Makefile的编写和在项目工程中使用Makefile(包括动态库、静态库的链接、整个工程联合编译)
通用Makefile的编写和在项目工程中使用Makefile(包括动态库、静态库的链接、整个工程联合编译)_makefile 同时编译二进制和库-CSDN博客
通用Makefile文件
https://www.cnblogs.com/sysu-blackbear/p/4034394.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

byxdaz

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

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

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

打赏作者

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

抵扣说明:

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

余额充值