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

推薦學習資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM