[C++]Makefile概要

#### Makefile 变量和赋值符
##延迟赋值:  =  变量的正常设置, 但值字段中提到的任何其他变量都在使用变量时用其值递归展开, 而不是声明变量时的值
##  延迟变量使用[=]操作符进行赋值, 在make解析Makefile阶段不会立即展开, 而是等到实际使用这个变量时才展开, 获得其真正的值.延迟展开变量一般用在规则的命令行中
##立即赋值: := 通过简单的内部值扩展来设置变量-在声明时扩展变量内的值.
##  立即变量使用[:=]操作符进行赋值, 在解析阶段就直接展开了, 顾名思义, 立即展开变量.立即变量一般用在规则中的目标、目标依赖中.
##条件赋值: ?= 一个变量如果没有被定义过, 就直接给它赋值; 如果之前被定义过, 那么这条赋值语句就什么都不做.
## 相当于:
## ifeq ($(origin VARIABLE), undefined)
##   VARIABLE = value
## endif
##追加赋值: += 一个变量以前已经被赋值, 现在想给它增加新的值, 此时可以使用+=追加赋值.
##  OBJS = hello.o
##  OBJS += module.o
##  等价于: OBJS = hello.o module.o

#### Makefile 通配符
## 在Makefile中可以使用的通配符有: * 、? 、[…].
## 在Makefile中, 通配符主要用在两个场合:
## 用在规则的目标和依赖中: make在读取Makefile时会自动对其进行匹配处理(通配符展开).如:
## test: *.o
##    gcc -o $@ $^
## *.o: *.c
##     gcc -c $
## 用在规则的命令中: 通配符的通配处理在shell执行命令时完成.如:
## clean:
##     rm -f *.o
## 除了以上两种情况, 在其他地方都不能直接使用通配符.需要一些函数(如wildcard)来实现.



#### Makefile 自动变量
## $@: 目标
## $^: 所有目标依赖
## $<: 目标依赖列表中的第一个依赖
## $?: 所有目标依赖中有更新的文件
## $(@D): 表示目标文件的目录部分
## $(@F): 表示目标文件的文件名部分
## $(*D): 在模式匹配中, 表示目标模式中%的目录部分
## $(*F): 在模式匹配中, 表示目标模式中%的文件名部分
## -: 告诉make在编译时忽略所有的错误
## @: 告诉make在执行命令前不要显示命令,关闭回声.也可以使用make -s参数

#### 环境变量
## make在解析Makefile中还会引入一些系统环境变量, 如编译参数CFLAGS、SHELL、MAKE等.
## 这些变量在make开始运行时被载入到Makefile文件中, 因为是全局性的系统环境变量, 所以这些变量对所有的Makefile都有效.
## 若Makefile中有用户自定义的同名变量, 系统环境变量将会被用户自定义的变量覆盖.可用?=进行赋值.

#### 条件判断
##ifeq 关键字
##  mode = debug
##  ifeq ($(mode),debug)
##ifneq 关键字
##  mode = debug
##  ifneq ($(mode),)
##ifdef 关键字
##  mode =
##  ifdef mode
##ifndef 关键字
##  mode =
##  ifndef mode
##没有else if, 可以用嵌套实现.

#### 函数
## 函数主要分为两类: make内嵌函数和用户自定义函数.
##   对于GNU make内嵌的函数, 直接引用就可以了;
##   对于用户自定义的函数, 要通过make的call函数来间接调用.
## 函数和参数列表之间要用空格隔开, 多个参数之间使用逗号隔开.
## 如果在参数中引用了变量, 变量的引用建议和函数引用使用统一格式: 要么是一对小括号$(XXX), 要么是一对大括号${XXX}.

##扩展通配符函数: wildcard
## 列出当前目录下所有符合PATTREN模式的文件名
##  $(wildcard PATTERN)
##  其中PATTREN可以使用shell能识别的通配符: ?、*等.
##   .PHONY: all
##   LIST  = $(wildcard *.c)
##   all:
##       @echo "LIST = $(LIST)"
##   运行结果为:
##   add.c  add.h  hello.c  main.c  makefile  sub.c  sub.h
##   LIST = hello.c main.c add.c sub.c

##subst函数
## subst函数用来实现字符串的替换, 将字符串text中的old替换为new.
## $(subst old,new,text)
## OBJ = $(subst .c,.o,$(SRC))

##patsubst函数
## patsubst函数主要用来模式替换: 使用通配符 % 代表一个单词中的若干字符.
##  目录下的所有.c文件名转换为以.o目标文件名
##  OBJ = $(patsubst %.c, %.o, $(SRC))

##模式匹配替换(写法不同, 功能等同于patsubst函数)
##  SRC := main.c sub.c
##  OBJ := $(SRC:%.c=%.o) 结果:OBJ = main.o sub.o

##strip函数
## strip函数是一个去空格函数. 空字符包括: 空格、多个空格、tab等不可显示的字符.
##   STR =     hello a    b   c  
##   STRIP_STR = $(strip $(STR)) 结果: STRIP_STR = hello a b c

##findstring 函数
## findstring函数用来查找一个字符串.使用格式如下:
## $(findstring FIND, IN)
## findstring函数会在字符串IN中查找"FIND"字符串, 如果找到, 则返回字符串FIND, 否则, 返回空.
##   STR =     hello a    b   c  
##   FIND = $(findstring hello, $(STR))
##   运行结果为: FIND = hello

##filter 函数
## filter函数用来过滤掉一个指定的字符串, 使用格式如下:
## $(filter PATTERN…,TEXT)
## filter函数用来过滤掉字符串TEXT中所有不符合PATTERN模式的单词, 只留下符合PATTERN格式的单词.
##   FILE = a.c b.h c.s d.cpp  
##   SRC = $(filter %.c, $(FILE))
##   运行结果为: SRC = a.c

##filter-out 函数
## filer-out函数是一个反过滤函数, 功能和filter函数恰恰相反:
##   该函数会过滤掉所有符合PATTERN模式的单词, 保留所有不符合此模式的单词.
##   FILE = a.c b.h c.s d.cpp  
##   SRC = $(filter-out %.c, $(FILE))
##   运行结果为: SRC = b.h c.s d.cpp

##sort函数: 单词排序
## $(sort LIST)
## sort函数对字符串LIST中的单词以首字母为准进行排序, 并删除重复的单词.
##   LIST = banana pear apple peach apple orange
##   SRC = $(sort $(LIST))
##   运行结果为: STR = apple banana orange peach pear

##word函数: 取单词
## word函数的作用是从一个字符串TEXT中, 按照指定的数目N取单词:
##  $(word N,TEXT)
## 函数的返回值是字符串TEXT中的第N个单词.如果N的值大于字符串中单词的个数, 返回空; 如果N为0, 则出错.
##   LIST = banana pear apple peach orange
##   word1 = $(word 1, $(LIST))
##   word5 = $(word 5, $(LIST))
##   word6 = $(word 6, $(LIST))
##   运行结果为:
##   word1 = banana
##   word5 = orange
##   word6 =

##wordlist函数: 取字串
## wordlist函数用来从一个字符串TEXT中取出从N到M之间的一个单词串:
##  $(wordlist N, M, TEXT)
## N和M都是从1开始的一个数字, 函数的返回值是字符串TEXT中从N到M的一个单词串.
## 当N比字符串TEXT中的单词个数大时, 函数返回空.
##   LIST = banana pear apple peach orange
##   sub_list = $(wordlist 1, 3, $(LIST))
##   运行结果为:
##   sub_list = banana pear apple

##words函数: 统计单词数目
## words函数用来统计一个字符串TEXT中单词的个数:
##  $(words TEXT)
## words函数的返回值为字符串TEXT中单词的个数.
##   LIST = banana pear apple peach orange
##   @echo "LIST len = $(words $(LIST))
##   运行结果为:
##   LIST len = 5

##firstword函数: 取首个单词
## firstword函数用来取一个字符串中的首个单词.
##  $(firstword NAMES…)
## firstword函数其实就相当于$(word 1,TEXT):
##   LIST = banana pear apple peach orange
##   @echo "first word = $(firstword $(LIST))"
##   运行结果为:
##   first word = banana

##dir函数: 取路径名的目录
## dir函数用来从一个路径名中截取目录的部分.
##  $(dir NAMES…)
## dir函数会从NAMES文件名序列中, 取出各个文件路径名中的目录部分并返回
##   .PHONY: all
##   LIST = /home/wit/banana.c /usr/include/stdio.h
##   all:
##       @echo "dir = $(dir $(LIST))"
##   运行结果为:
##   dir = /home/wit/ /usr/include/

##notdir函数: 取文件名
## notdir函数和dir函数实现完全相反的功能: 从一个文件路径名中去文件名, 而不是目录.notdir函数的使用方法和dir函数相同.
##   .PHONY: all
##   LIST = /home/wit/banana.c /usr/include/stdio.h
##   all:
##       @echo "file = $(notdir $(LIST))"
##   运行结果为:
##   file = banana.c stdio.h

##suffix函数: 取文件名后缀
## suffix函数从一系列文件名序列中, 取出各个文件名的后缀.
## $(suffix NAMES…)
##   .PHONY: all
##   LIST = /home/wit/banana.c /usr/include/stdio.h
##   all:
##       @echo "suffix = $(suffix $(LIST))"
##   运行结果为:
##   suffix = .c .h

##basename函数: 取文件名前缀
## basename函数从一系列文件名序列中, 取出各个文件名的前缀部分:
## $(basename NAMES…)
## 如果一个文件名中包括多个点号, basename函数返回最后一个点号之前的文件名部分; 如果一个文件名没有前缀, 函数返回空字符串.
##   .PHONY: all
##   LIST = /home/wit/banana.c /usr/include/vmlinux.lds.S
##   all:
##       @echo "basename = $(basename $(LIST))"
##   运行结果为:
##   basename = /home/wit/banana /usr/include/vmlinux.lds

##addsuffix函数: 给文件名加后缀
## addsuffix函数的作用是: 给文件列表中的每个文件名添加后缀SUFFIX
## $(addsuffix SUFFIX, NAMES…)
##   .PHONY: all
##   LIST = apple banana peach
##   all:
##       @echo "addsuffix = $(addsuffix .c, $(LIST))"
##   运行结果为:
##   addsuffix = apple.c banana.c peach.c

##addprefix函数: 给文件名加前缀
## addprefix函数的作用是: 给文件列表中的每个文件名添加一个前缀PREFIX
## $(addprefix PREFIX, NAMES…)
##   .PHONY: all
##   LIST = apple.c banana.c peach.c
##   all:
##       @echo "addsuffix = $(addprefix /home/wit/, $(LIST))"
##   运行结果为:
##   addsuffix = /home/wit/apple.c /home/wit/banana.c /home/wit/peach.c

##join函数: 单词连接
## 将字符串LIST1和字符串LIST2的各个单词依次连接, 合并为新的单词构成的字符串
## $(join LIST1,LIST2)
##   .PHONY: all
##   LIST1 = apple banana peach
##   LIST2 = .c .h .s
##   LIST  = $(join $(LIST1), $(LIST2))
##   all:
##       @echo "LIST = $(LIST)"
##   运行结果为:
##   LIST = apple.c banana.h peach.s
##   如果两个字符串中的单词个数不相等, 则只合并前面的单词, 剩下的单词不合并.
 
---makefile例子
#此项目源文件后缀类型
PROJECTTYPE:=.cpp

#C语言编译器
CC:= gcc
#C++编译器
CXX:= g++
#C语言配置参数
CFLAGS?= -g -pedantic -std=c11 -Wall -o
#C++配置参数
#CXXFLAGS?= -g -Wall -std=c++14
CXXFLAGS?= -g -std=c++14 -O0

#RM:= rm -rf
RM:= del /F /S /Q

#获取当前makefile绝对路径
pes_parent_dir:=$(shell chdir)

target?=VSCodeCPPLearningMakefile.exe
BIN:=$(pes_parent_dir)/$(target)

##想编译的文件目录
##手动输入: 所有目录及子目录都需加上
#SRC_DIRS  = ./util ./Base ./Arithmetic ./DesignPattern/01_FactoryMethod ... ./Modern/ ./Modern/Thread
##递归遍历:
#SRC_DIRS := $(shell find ../a -maxdepth 3 -type d) $(shell find ../b -maxdepth 3 -type d)
SRC_DIRS:= $(shell dir /S /AD /B)

##EX_FILES要排除的文件
#EX_FILES = ../a/aa/exa.cpp ../a/bb/exb.cpp
EX_FILES:=

##foreach 遍历想编译的源目录, wildcard 获取目录下所有文件并加上后缀.cpp(语法上是为.cpp加上前缀, 前缀为目录下的文件名, 即为文件加后缀)
#SRC_FILES  = $(filter-out ${EX_FILES}, $(foreach dir, $(SRC_DIRS), $(wildcard $(addprefix $(dir)/*, .cpp))))
SRC_FILES:=$(filter-out $(EX_FILES), $(foreach dir, $(SRC_DIRS), $(wildcard $(addprefix $(dir)/*, $(PROJECTTYPE)))))

#basename 返回一个字符串. 之前的所有字段,即去掉后缀, 同时addsuffix 加上后缀.o
#OBJS = $(addsuffix .o, $(basename $(SRC_FILES)))
OBJS=$(patsubst %$(PROJECTTYPE),%.o,$(SRC_FILES))

#头文件搜索路径
INCLUDE_PATH:=$(foreach n,$(SRC_DIRS), -I$(n))

#-c: 生成xxx.o的目标文件 -o: 生成可执行程序的目标文件
$(BIN):$(OBJS)
    @echo bulding....
    @$(CXX) $(CXXFLAGS) CPPLearning.cpp $(OBJS) -o $(target)
    @echo created file: $(target)

#   @$(CXX) -o $(target) $(OBJS)  $(CXXFLAGS)
#   @$(CXX) -o $@ $^  $(CXXFLAGS)
#   @$(CXX) $^ -o $@
#   $(COMPILE.CXX) -c $^ -o $@

#$(SRC_DIRS)/%.o:$(SRC_DIRS)/%.cpp
$(OBJS): $(SRC_FILES)
#   @$(CXX) -c -o %.o %.cpp

#.PHONY 伪目标, 目标文件名存在也可以执行
.PHONY: clean show cleanexe
clean:
    @echo 'Cleaning up all files...'
    @-$(RM) $(target)
    @-$(RM) *.o
    @echo 'Clean done'
cleanexe:
    @echo 'Cleaning up exe file...'
    @-$(RM) $(target)
    @echo 'Clean done'

show:
    @echo target======
    @echo    $(target)
    @echo SRC_DIRS====
    @echo    $(SRC_DIRS)
    @echo SRC_FILES===
    @echo    $(SRC_FILES)
    @echo EX_FILES====
    @echo    $(EX_FILES)
    @echo OBJS========
    @echo    $(OBJS)

posted on 2023-01-29 18:17  tony_cyou  阅读(58)  评论(0编辑  收藏  举报

导航