makefile 完美教程

简介

Makefile 是和 make 命令一起配合使用的,很多大型项目的编译都是通过 Makefile 来组织的,。

我建立工程的方法有以下三点:

1.makefile:

优点:使用非常广泛,通用性强,可跨平台。

缺点:语法比较蛋疼。要写出一个通用,便于管理,且兼容性强的makefile比较困难。

2.cmake:

优点:简单易用,使用较广泛,方便管理,可跨平台。

缺点:自动生成的makefile太臃肿。

3.sh脚本:

优点:自由,高度定制。简单易用,可操作性强。方便维护。(甚至还可以生成makefile)

缺点:sh建的工程太少了(估计没人这么搞吧)

但我考虑到方便移植和管理其他人的工程还是选择了第一种,以makefile建立管理工程。(其实我的内心是比较向往第三种sh脚本建工程的)。下面来介绍下makefile的规则以及语法

PS:我本意是想写短一点的,只是想写点常用的东西,方便大家查阅。精炼了这么久,可还有这么多内容(15000字)。所以,大家还是耐心的学习吧,想学好linux这是必不可少的一步。

规则

说明

.... : ...

[TAB]

[TAB]

...

target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。

就是,要生成那个target所需要的文件或是目标。

也就是make需要执行的命令。(任意的shell命令)

这是一个文件的依赖关系,也就是说,这一个或多个的目标文件依赖于中的文件,其生成规则定义在 中。说白一点就是说,中如果有一个以上的文件比文件要新的话,所定义的命令就会被执行。这就是makefile的规则。也就是makefile中最核心的内容。

经典示例

原始版本

objects = main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

edit : $(objects)

cc -o edit $(objects)

main.o : main.c defs.h

cc -c main.c

kbd.o : kbd.c defs.h command.h

cc -c kbd.c

command.o : command.c defs.h command.h

cc -c command.c

display.o : display.c defs.h buffer.h

cc -c display.c

insert.o : insert.c defs.h buffer.h

cc -c insert.c

search.o : search.c defs.h buffer.h

cc -c search.c

files.o : files.c defs.h buffer.h command.h

cc -c files.c

utils.o : utils.c defs.h

cc -c utils.c

clean :

rm edit $(objects)

自动推导版本

objects = main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

cc = gcc

edit : $(objects)

cc -o edit $(objects)

main.o : defs.h

kbd.o : defs.h command.h

command.o : defs.h command.h

display.o : defs.h buffer.h

insert.o : defs.h buffer.h

search.o : defs.h buffer.h

files.o : defs.h buffer.h command.h

utils.o : defs.h

.PHONY : clean

clean :

rm edit $(objects)

理解

1.内容展开

list_a = 1.a 2.a

list_b = 1.b 2.b 3.b

#list_a中的元素每次单个展开连接list_b中的所有元素

#结果如下

test:list_a

%.a:list_b #展开:

1.a:1.b 2.b 3.b #此时&@=1.a $<=1.b 2.b 3.b

2.a:1.b 2.b 3.b #此时&@=2.a $<=1.b 2.b 3.b

#内容还可以继续展开

%.b:%.x #自动推导展开

1.b:1.x

2.b:2.x

3.b:3.x

2.include 相当于将内容复制展开,export可以共享全局变量。

3.用make遍历子目录makefile时相当于,用新进程开启make。因此export不能共享全局变量。

注意

1.注意空格与TAB(有些编辑器会自动将TAB转换成4个空格,难以发现)

2.注意 = 与 := 的区别

包括export 在内的所有变量都不能共享

如果使用cd &&make进行遍历。那么最好转换成绝对路径

3.如果使用cd &&make进行遍历。那么最好转换成绝对路径

语法

1.符号

1) 命令前缀

[不用前缀 ]输出执行的命令以及命令执行的结果, 出错的话停止执行

[前缀 @]只输出命令执行的结果, 出错的话停止执行

[前缀 -]命令执行有错的话, 忽略错误, 继续执行

2) 通配符

*表示任意一个或多个字符

? 表示任意一个字符

[...] [abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字

3) 自动变量

在下文的 自动变量

4) 赋值

= 与 :=

相同点:都是给内容赋值;

区别:与Verilog的=和<=类似。

其中 = 和 := 的区别在于 :

= 只能使用前面定义好的变量,

= 可以使用后面定义的变量

赋值符号 [=]

= 最后再赋值

# Makefile内容

OBJS2 = $(OBJS1) programC.o

OBJS1 = programA.o programB.o

all:

@echo $(OBJS2)

# bash中执行 make, 可以看出虽然 OBJS1 是在 OBJS2 之后定义的, 但在 OBJS2中可以提前使用

$ make

programA.o programB.o programC.o

赋值符号 [:=]

:= 立即赋值

# Makefile内容

OBJS2 := $(OBJS1) programC.o

OBJS1 := programA.o programB.o

all:

@echo $(OBJS2)

# bash中执行 make, 可以看出 OBJS2 中的 $(OBJS1) 为空

$ make

programC.o

赋值符号 [+=]

+= 变量追加值

# Makefile内容

SRCS := programA.c programB.c programC.c

SRCS += programD.c

all:

@echo "SRCS: " $(SRCS)

# bash中运行make

$ make

SRCS: programA.c programB.c programC.c programD.c

5) 伪目标

.PHONY

.PHONY : clean

clean :

-rm edit $(objects)

2. 隐含规则

1) 自动推倒命名:

编译C时,*.o 的目标会自动推导为 *.c

2) 隐含变量

[RM] rm -f

[AR] ar

[CC] cc

[CXX] g++

[ARFLAGS] AR命令的参数

[CFLAGS] 语言编译器的参数

[CXXFLAGS] C++语言编译器的参数

3) 自动变量

[$@] 目标集合

[$%] 当目标是函数库文件时, 表示其中的目标文件名

[$<] 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标

[$?] 比目标新的依赖目标的集合

[$^] 所有依赖目标的集合, 会去除重复的依赖目标

[$+] 所有依赖目标的集合, 不会去除重复的依赖目标

[$*] 这个是GNU make特有的, 其它的make不一定支持

1.o: 1.c

@echo $@ : $<

# bash中执行 make:

1.o : 1.c

3.定义

不限于makefile还有部分shell指令

1) 查看依赖关系

gcc -MM

$ gcc -MM kvm_main.c

kvm_main.o: kvm_main.c iodev.h coalesced_mmio.hasync_pf.h

#这句就可以加到 Makefile 中作为编译 kvm_main.o 的依赖关系

2) 定义命令包

define

command

...

endef

# Makefile 内容

define run-hello-makefile

@echo -n "Hello"

@echo " Makefile!"

@echo "这里可以执行多条 Shell 命令!"

endef

all:

$(run-hello-makefile)

# bash 中运行make

$ make

Hello Makefile!

这里可以执行多条 Shell 命令!

3) 条件判断

# Makefile 内容

all:

ifeq ("aa", "bb")

@echo "equal"

else

@echo "not equal"

endif

# bash 中执行 make

$ make

not equal

4) 自定义函数

$(call ,,,...)

# Makefile 内容

hello = "hello " $(1) " world"

all:

@echo $(call hello,"我是参数1")

# bash 中执行 make

$ make

hello 我是参数1 world

5) 执行shell指令

$(shell ) 与 ``

作用:执行一个shell命令, 并将shell命令的结果作为返回.

注意:` 是反引号(英文状态下,键盘的ESC下面那个键)

4.函数

不限于makefile还有部分shell指令

1) 传参(同一个进程)

export

export EX_VAR = value

export EX_VAR := value

export EX_VAR += value

#= := += 与上面的描述基本相同

注意:是同一个进程下的make才有用。当多级遍历make时是无法全局的。

2) 字符串处理

(1) 字符串替换函数

$( subst,,)

功能: 把字符串 中的 替换为

返回: 替换过的字符串

# Makefile 内容

all:

@echo $(subst t,e,maktfilt) <-- 将t替换为e

# bash 中执行 make

$ make

makefile

(2) 字符串中每个元素替换函数

$( patsubst ,,)

功能: 把字符串 中的的每个元素 替换为

返回: 替换过的字符串

# Makefile 内容

all:

@echo $(patsubst %.c,%.o,programA.c programB.c)

# bash 中执行 make

$ make

programA.o programB.o

(3) 去空格函数

$(strip )

功能: 去掉 字符串中开头和结尾的空字符

返回: 被去掉空格的字符串值

# Makefile 内容

VAL := " aa bb cc "

all:

@echo "去除空格前: " $(VAL)

@echo "去除空格后: " $(strip $(VAL))

# bash 中执行 make

$ make

去除空格前: aa bb cc

去除空格后: aa bb cc

(4) 判断字符串内是否存在特定字符串

$(findstring,)

功能: 在字符串 中查找 字符串是否存在

返回: 如果找到, 返回 字符串, 否则返回空字符串

# Makefile 内容

VAL := " aa bb cc "

all:

@echo $(findstring aa,$(VAL))

@echo $(findstring ab,$(VAL))

# bash 中执行 make

$ make

aa

3) 文件元素相关

(1) 取文件函数

保留符合条件的元素

$(filter ,)

# Makefile 内容

all:

@echo $(filter %.o %.a,program.c program.o program.a)

# bash 中执行 make

$ make

program.o program.a

program.c

去掉符合条件的元素

$(filter-out ,)

# Makefile 内容

all:

@echo $(filter-out %.o %.a,program.c program.o program.a)

# bash 中执行 make

$ make

program.c

获取该目录下所有文件

获取该目录下所有.x文件

$(wildcard *.x)

# Makefile 内容

all:

@echo $(wildcard *.c)

# bash 中执行 make

$ make

a.x b.x c.x

(2) 路径函数

去掉路径

$(notdir )

功能: 去掉序列的路径

返回: 文件名序列 中的非目录部分

# Makefile 内容

all:

@echo $(notdir /home/a.c ./bb.c ../c.c d.c)

# bash 中执行 make

$ make

a.c bb.c c.c d.c

取路径

$(dir )

功能: 从文件名序列 中取出目录部分

返回: 文件名序列 中的目录部分

# Makefile 内容

all:

@echo $(dir /home/a.c ./bb.c ../c.c d.c)

# bash 中执行 make

$ make

/home/ ./ ../ ./

(3) 取前后缀

取后缀函数

$(suffix )

功能: 从文件名序列 中取出各个文件名的后缀

返回: 文件名序列 中各个文件名的后缀, 没有后缀则返回空字符串

# Makefile 内容

all:

@echo $(suffix /home/a.c ./b.o ../c.a d)

# bash 中执行 make

$ make

.c .o .a

取前缀函数

$(basename )

功能: 从文件名序列 中取出各个文件名的前缀

返回: 文件名序列 中各个文件名的前缀, 没有前缀则返回空字符串

# Makefile 内容

all:

@echo $(basename /home/a.c ./b.o ../c.a /home/.d .e)

# bash 中执行 make

$ make

/home/a ./b ../c /home/

(3) 增添前后缀

增添后缀函数

$(addsuffix ,)

功能: 把后缀 加到 中的每个单词后面

返回: 加过后缀的文件名序列

# Makefile 内容

all:

@echo $(addsuffix .c,/home/a b ./c.o ../d.c)

# bash 中执行 make

$ make

/home/a.c b.c ./c.o.c ../d.c.c

增添前缀函数

$(addprefix , )

功能: 把前缀 加到 中的每个单词前面

返回: 加过前缀的文件名序列

# Makefile 内容

all:

@echo $(addprefix test_,/home/a.c b.c ./d.c)

# bash 中执行 make

$ make

test_/home/a.c test_b.c test_./d.c

我写的管理大型项目的makefile

以下内容为基础版本。可以混编C和C++。

但没有加入平台兼容性验证等功能。

在ubuntu16.04下测试通过。

tree

.

├── app

│ ├── app1

│ │ ├── main.c

│ │ └── makefile

│ └── makefile

├── make_conf.mk

├── makefile

├── make_fun.mk

├── module

│ ├── makefile

│ └── test

│ ├── drive

│ │ ├── cpp_test.cpp

│ │ ├── cpp_test.h

│ │ ├── test.c

│ │ └── test.h

│ ├── hal

│ │ ├── test_hal.c

│ │ └── test_hal.h

│ └── makefile

└── output

├── bin

│ ├── app1.bin

│ └── app2.bin

├── lib

│ └── libobj.a

└── obj

├── app

│ ├── app1

│ │ └── main.o

│ └── app2

│ └── main.o

└── module

└── test

├── drive

│ ├── cpp_test.o

│ └── test.o

└── hal

└── test_hal.o

17 directories, 22 files

顶层 makefile

# -------------------------------------------

# FileName :makefile

# Author :wind

# Date :2018-1-16

# Description

#

# Top makefile.

#

# -------------------------------------------

# 设置当前路径

DIR_ROOT:=.

# 设置递归子目录

DIR_LIST_SUB :=module app

include $(DIR_ROOT)/make_conf.mk

all:make_root

clean:make_clean

include $(DIR_ROOT)/make_fun.mk

中间遍历层 makefile

# -------------------------------------------

# FileName :xx/makefile

# Author :wind

# Date :2018-1-16

# Description

#

# Level1 makefile.

#

# -------------------------------------------

# 设置当前路径

DIR_ROOT:=..

# 添加递归子目录

DIR_LIST_SUB :=\

app1\

app2\

# 添加源文件

FILE_LIST_C +=\

FILE_LIST_CPP +=\

FILE_LIST_S +=\

# 添加头文件路径

DIR_LIST_INCLUDE+=\

include $(DIR_ROOT)/make_conf.mk

all:make_show make_subdir

clean:make_clean

include $(DIR_ROOT)/make_fun.mk

app的makefile

# -------------------------------------------

# FileName :xx/xx/makefile

# Author :wind

# Date :2018-1-16

# Description

#

# Level2 makefile.

#

# -------------------------------------------

# 设置当前路径

DIR_ROOT:=../..

# 添加递归子目录

DIR_LIST_SUB :=\

# 添加源文件

FILE_LIST_C +=\

main.c\

FILE_LIST_CPP +=\

FILE_LIST_S +=\

# 添加头文件路径

DIR_LIST_INCLUDE+=\

$(DIR_ROOT)/module/test/hal\

$(DIR_ROOT)/module/test/drive\

include $(DIR_ROOT)/make_conf.mk

all:make_show make_app

clean:make_clean

include $(DIR_ROOT)/make_fun.mk

drive的makefile

# -------------------------------------------

# FileName :xx/xx/makefile

# Author :wind

# Date :2018-1-16

# Description

#

# Level2 makefile.

#

# -------------------------------------------

# 设置当前路径

DIR_ROOT:=../..

# 添加递归子目录

DIR_LIST_SUB :=\

# 添加源文件

FILE_LIST_C +=\

drive/test.c\

hal/test_hal.c\

FILE_LIST_CPP +=\

drive/cpp_test.cpp\

FILE_LIST_S +=\

# 添加头文件路径

DIR_LIST_INCLUDE+=\

hal\

drive\

include $(DIR_ROOT)/make_conf.mk

all:make_show make_lib_a

clean:make_clean

include $(DIR_ROOT)/make_fun.mk

配置文件 make_conf.mk

# -------------------------------------------

# FileName :make_set.inc

# Author :wind

# Date :2018-1-16

# Description

#

# 工程相关设置。

#

# -------------------------------------------

# 设置常用指令

# -------------------------------------------

RM = rm -f

MV = mv -f

MKDIR = mkdir -p

RMDIR = rm -rf

# 颜色输出

# -------------------------------------------

ECHO_END:=\033[0m"

ECHO_GREEN:=echo "\033[32m

ECHO_RED:=echo "\033[31m

ECHO_YELLOW:=echo "\033[33m

ECHO_BLUE:=echo "\033[34m

ECHO_GREEN_YELLOW:=echo "\033[42;30m

# 编译缺省设置

# -------------------------------------------

# 默认编译

CXX:=g++

#编译选项

COMPILE_C ?= $(CXX)

COMPILE_CXX ?= $(CXX)

COMPILE_ASM ?= $(CXX)

COMPILE_AR ?= ar

# 设置优化等级

OPT ?=0

# 设置警告开关

COMPILE_WARN ?= -Wall

# 设置静态编译

COMPILE_STATIC ?= -s

# 在环境基础下添加设置

CFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC)

CXXFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC)

ASFLAGS+= -Wa,-adhlns=$(<:.S=.lst),-gstabs $(DIR_LIST_INCLUDE_I)

# 编译设置汇总

COMPILE_CFLAGS = $(CFLAGS)

COMPILE_CXXFLAGS = $(CXXFLAGS)

COMPILE_ASFLAGS = $(ASFLAGS)

#链接选项

LDFLAGS+= -lstdc++#编译C++

LDFLAGS+= -lpthread#使用了线程

LDFLAGS+= -fPIC#编译为位置独立的代码

LDFLAGS+= -ldl#引用动态库

LDFLAGS+= $(DIR_LIST_INCLUDE_I)

#引用其他静态库

FILE_LIST_LIB_A+=\

功能文件 make_fun.mk

# -------------------------------------------

# FileName :make_fun.mk

# Author :wind

# Date :2018-1-16

# Description

#

# 实际编译过程。

#s

# -------------------------------------------

# 路径关系

# -------------------------------------------

DIR_ROOT_REAL=$(realpath $(DIR_ROOT))

NAME_MODULE := $(notdir $(CURDIR))#所在的文件夹名称

DIR_OUTPUT:=$(DIR_ROOT)/output

DIR_BIN:=$(DIR_OUTPUT)/bin

DIR_OBJ:=$(DIR_OUTPUT)/obj

DIR_LIB:=$(DIR_OUTPUT)/lib

# 路径处理成可用参数

# -------------------------------------------

DIR_LIST_INCLUDE_I:=$(addprefix -I,$(DIR_LIST_INCLUDE))#添加编译选项-I

DIR_LIST_SUB:=$(addprefix $(CURDIR)/,$(DIR_LIST_SUB))#转换成绝对路径

DIR_CURDIR:=$(subst $(DIR_ROOT_REAL),,$(CURDIR))#相对路径

DIR_OBJ_OUT:=$(DIR_OBJ)$(DIR_CURDIR)

#文件处理

FILE_LIST_OBJ:=$(FILE_LIST_C:%.c=%.o)

FILE_LIST_OBJ+=$(FILE_LIST_CPP:%.cpp=%.o)

FILE_LIST_OBJ+=$(FILE_LIST_S:%.s=%.o)

FILE_LIST_OBJ:=$(addprefix $(DIR_OBJ_OUT)/,$(FILE_LIST_OBJ))#转换成带绝对路径的中间文件

FILE_LIB_A:=$(DIR_LIB)/libobj.a

FILE_LIST_LIB_APP:=$(DIR_BIN)/$(NAME_MODULE).bin

# 函数库

# -------------------------------------------

make_root:make_start make_subdir make_end

# 工程开始

make_start:

@$(ECHO_BLUE)\t-----------------------------$(ECHO_END)

@$(ECHO_BLUE)\t-\t [编译开始] \t -$(ECHO_END)

@$(ECHO_BLUE)\t-----------------------------$(ECHO_END)

@$(ECHO_BLUE)[COMPILE_C]$(COMPILE_C) [COMPILE_CFLAGS]$(COMPILE_CFLAGS)$(ECHO_END)

@$(ECHO_BLUE)[COMPILE_CXX]$(COMPILE_CXX) [COMPILE_CXXFLAGS]$(COMPILE_CXXFLAGS)$(ECHO_END)

@$(ECHO_BLUE)[COMPILE_ASM]$(COMPILE_ASM) [COMPILE_ASFLAGS]$(COMPILE_ASFLAGS)$(ECHO_END)

@$(ECHO_BLUE)[COMPILE_AR]$(COMPILE_AR) [LDFLAGS]$(LDFLAGS)$(ECHO_END)

@$(ECHO_BLUE)[OPT]$(OPT) $(ECHO_END)

@$(ECHO_BLUE)[COMPILE_STATIC]$(COMPILE_STATIC) $(ECHO_END)

@$(ECHO_BLUE)[FILE_LIST_LIB_A]$(FILE_LIST_LIB_A) $(ECHO_END)

# 工程完成

make_end:

@$(ECHO_BLUE)[编译完成]$(ECHO_END)

# 递归子目录

make_subdir:

@for list in $(DIR_LIST_SUB);\

do\

cd $$list && make all|| exit 1;\

done

# 生成可执行文件

make_app:make_lib_a make_bin

# 生成静态链接库

make_lib_a:make_obj

@$(MKDIR) `dirname $(FILE_LIB_A)`

@$(ECHO_YELLOW)[$(COMPILE_AR)]-rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)$(ECHO_END)

$(COMPILE_AR) -rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)

# 链接

make_bin:$(FILE_LIST_LIB_APP)

# 编译

make_obj:$(FILE_LIST_OBJ)

# 清除

make_clean:

$(RMDIR) $(DIR_OUTPUT)

# 显示参数,方便调试

make_show:

@$(ECHO_GREEN_YELLOW)[DIR_CURDIR] $(DIR_CURDIR) $(ECHO_END)

@$(ECHO_GREEN)[FILE_LIST_C] $(FILE_LIST_C) $(ECHO_END)

@$(ECHO_GREEN)[DIR_LIST_INCLUDE] $(subst $(DIR_ROOT)/,[DIR_ROOT]/,$(DIR_LIST_INCLUDE)) $(ECHO_END)

# 文件操作过程

# -------------------------------------------

# 编译过程成中间文件

$(DIR_OBJ_OUT)/%.o: %.c

@$(MKDIR) `dirname $@`

@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)

$(COMPILE_C) -c $(COMPILE_CFLAGS) $< -o $@

$(DIR_OBJ_OUT)/%.o: %.cpp

@$(MKDIR) `dirname $@`

@$(ECHO_YELLOW)[$(COMPILE_CXX)]$< -o $@ $(ECHO_END)

$(COMPILE_CXX) -c $(COMPILE_CXXFLAGS) $< -o $@

$(DIR_OBJ_OUT)/%.o: %.s

@$(MKDIR) `dirname $@`

@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)

$(COMPILE_ASM) -c $(COMPILE_ASFLAGS) $< -o $@

# 链接成二进制文件

$(FILE_LIST_LIB_APP): $(FILE_LIB_A)

@$(MKDIR) `dirname $@`

@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)

$(COMPILE_C) $(LDFLAGS) -L$(DIR_LIB) -lobj $< $(FILE_LIST_LIB_A) -o $@

内容推荐

跟我一起写Makefile

Makefile 使用总结

项目实用makefile

Makefile之大型工程项目子目录Makefile的一种通用写法

引用本地址

https://www.cnblogs.com/wittxie/p/9836097.html

Copyright © 2088 2006世界杯决赛_世界杯预选赛欧洲区积分榜 - meibada.com All Rights Reserved.
友情链接