Makefile:生成多目标文件的方法

在网上已经搜到了最简单的方法,不过带着路径的情况下自己没有改成功。先分享原模板

然后看下我的文件目录结构

test/
├── bin
├── Makefile
└── src
    ├── a.cpp
    └── b.cpp

然后我失败的模仿(总是只生成一个目标文件)

CXX = g++
CXX_FLAGS = -Wall -g  

SRC = $(wildcard ./src/*.cpp)
NOTDIR_SRC  = $(notdir $(SRC))
OBJS = $(patsubst %.cpp, ./bin/%.o, $(NOTDIR_SRC))

# 多目标
TARGET_LIST = $(patsubst %.cpp, %, $(NOTDIR_SRC))
$(TARGET_LIST):$(OBJS)
	@echo $@
	$(CXX) -o ./bin/$@ ./bin/$@.o
	@echo $(TARGET_LIST)

./bin/%.o:./src/%.cpp
	$(CXX) -c  $(CFLAGS) $< -o $@ 
 
.PHONY:clean  
clean:  
	-rm -f ./bin/*

上面的Makefile看上去应该是有两个目标文件,然后应该编译两个.cpp文件生成两个可执行文件才对,可是咋总生成一个目标文件呢,目标文件队列中的第二个直接被忽略了。。。。 幸好,一口Linux群里有大佬帮忙解答了:

Makefile只有一个总目标,然后由多个可执行文件组成,在把总目标隐式申明,就行了,你写一个总目标
原来,Makefile虽然支持多目标,但是人家支持的方式是总目标必须只有一个,然后总目标由多个可执行文件组成就好了。于是,修改如下:

CXX = g++
CXX_FLAGS = -Wall -g  
SRC = $(wildcard ./src/*.cpp)
NOTDIR_SRC  = $(notdir $(SRC))
OBJS = $(patsubst %.cpp, ./bin/%.o, $(NOTDIR_SRC))

.PHONY:all
# 多目标
TARGET_LIST = $(patsubst %.cpp, %, $(NOTDIR_SRC))
all:$(TARGET_LIST)
$(TARGET_LIST):$(OBJS)
	@echo $@
	$(CXX) -o ./bin/$@ ./bin/$@.o
	@echo $(TARGET_LIST)
./bin/%.o:./src/%.cpp
	$(CXX) -c  $(CFLAGS) $< -o $@  
.PHONY:clean  
clean:  
	-rm -f ./bin/*

一切正常。
顺带,记录下大佬的博客.
此外,还有其他人也回答了

Makefile是执行第一个目标文件,然后根据第一个目标文件的依赖往下找。Makefile只彻底执行完第一个目标文件(包括找依赖)之后就以为活干完了不干活了。

后续

第二天,再次读《跟我一起写Makefile》的静态模式这一块,发现之前对静态模式理解有误,静态模式的语法是这样的:

<targets ...> : <target-pattern> : <prereq-patterns ...>
    <commands>
    ...

还举了个例子:

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

我理解成了targets为foo、bar,target-pattern为foo.o、bar.o, prereq-patterns为foo.c、bar.c。我的错误理解是目标文件、一次依赖文件、二次依赖文件,真是犯傻了,如果一次依赖文件是.o二次依赖文件是.c这种何必引入这种模式呢,直接依赖.c又不是不可以。。。。人家真实用途target-pattern是用来对目标文件进行说明的,比如后缀都是.o等,但是如果没有什么共同特点的话这个说明就直接用%就好了嘛,表示目标文件们没有共同点(个人理解)
如此以来,我们写出正确的静态模式的Makefile:

CXX = g++
CXX_FLAGS = -Wall -g  
SRC = $(wildcard ./src/*.cpp)
NOTDIR_SRC  = $(notdir $(SRC))

TARGETS = $(patsubst %.cpp, %, $(NOTDIR_SRC))
all:$(TARGETS)
#静态模式的正确理解:<目标文件> <目标文件的模式>  <目标的依赖模式>
$(TARGETS) : % : ./src/%.cpp
	$(CXX) -c  $(CFLAGS) $< -o ./bin/$@ 	
.PHONY:clean  
clean:  
	-rm -f ./bin/*

变量定义知识补充

1. =(递归展开方式)

简单的使用 = 号,在 = 左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值

  • 优点:把变量的真实值推到后面来定义
  • 缺点:递归定义会让make陷入无限的变量展开过程中去
2. :=(简单方式)

这个是推荐使用的方式,其实这种方法的使用效果与C语言相同(变量定义了,接下来使用它的时候其值就是刚才定义的值,如果后面这个变量又被重新定义了那么只有重新定义之后的部分使用它的新值)。

  • 前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
3. ?=

如果变量没有被定义过,那么此刻定义变量;如果变量先前被定义过,那么这条语将什么也不做

Makefile思想的感悟

  • 其实人家的思想就是根据第一个目标文件去找依赖,执行命令而已
  • 不要乱用,如果出了想不明白的错误,就开始按照总目标文件及依赖文件逐步向后捋,过程中注意关注依赖文件真的能被识别吗,千万别乱用%以为能找的到

Makefile知识加油站

  • .PHONY:clean被称之伪目标,原来它真正的用途是避免由于名称相同带来的清理工作不生效问题

假如自己的工程目录也有一个名字叫clean的文件,而Makefile中伪目标clean通常并没有依赖文件,所以它认为既然已经有目标文件了,那我就不用干活了,于是本该清理的操作没有被执行。还有就是Makefile的习惯用法中有个all目标,其实也可以放到伪目标里面,如.PHONY all clean

推荐学习资料

posted @ 2022-04-08 22:21  时间的风景  阅读(1601)  评论(0编辑  收藏  举报