昨天編譯一個工程,Makefile放在工程目錄,.cpp文件放在/source目錄下,頭文件.h放在/include目錄下。遇到了幾個問題,一番修改,終於改好,整理如下:
出問題的Makefile如下:
INC = -I./include
CFLAGS := -O0 -g $(INC)
SOURCE = $(wildcard ./source/*.cpp)
CPPFILES = $(notdir $(SOURCE))
OBJ = $(patsubst %.cpp, %.o, $(CPPFILES))
EXE = main
$(EXE):$(OBJ)
g++ -std=c++11 -o $@ $^
%.o:%.cpp
g++ -std=c++11 -o $@ -c $^ -I. $(CFLAGS)
.PHONY:clean
clean:
-rm $(EXE) *.o
編譯結果:
make: *** No rule to make target `func1.o', needed by `main'. Stop.
無法生成目標文件main.o,是因爲編譯器找不到源文件,因爲編譯器默認是在當前目錄下尋找源文件。但我們在Makefile中,通過notdir函數將源文件的目錄清除掉了。在以上Makefile中“EXE = main”後面加個打印:
PRINT:
@echo "CPPFILES:"
@echo $(CPPFILES)
然後執行make或make PRINT,得到結果如下圖所示,可見用於編譯的源文件都是不帶路徑的。
解決方法1 保留源碼路徑
既然上面一小節的錯誤是由於編譯器找不到源碼路徑,那如果我們把源碼路徑保留下來呢?看下面這個Makefile:
INC = -I./include
CFLAGS := -O0 -g $(INC)
SOURCE = $(wildcard ./source/*.cpp)
#CPPFILES = $(notdir $(SOURCE))
#OBJ = $(patsubst %.cpp, %.o, $(CPPFILES))
OBJ = $(patsubst %.cpp, %.o, $(SOURCE))
EXE = main
#PRINT:
# @echo "CPPFILES:"
# @echo $(CPPFILES)
$(EXE):$(OBJ)
g++ -std=c++11 -o $@ $^
%.o:%.cpp
g++ -std=c++11 -o $@ -c $^ -I. $(CFLAGS)
.PHONY:clean
clean:
-rm $(EXE) *.o
執行make,結果如下:
編譯成功,各cpp文件對應的.o文件都放在了源碼路徑下,最終的可執行文件放在當前路徑。
解決方法2 使用VPATH
方法1中保留了源碼路徑,那麼如果希望編譯出來的.o文件也放在當前路徑下怎麼辦?仍然是要去掉源碼路徑,那麼這時候編譯器去哪裏找源碼呢?這時候可以考慮用VPATH。VPATH用於設置全局訪問路徑。make可識別一個特殊變量“VPATH”,通過"VPATH"可以指定依賴文件的搜索路徑。變量“VPATH”的定義中,使用空格或者冒號“:”將多個目錄分開,例如 VPATH = include:source。make搜索目錄的順序爲:當前目錄、VPATH中定義的目錄順序。
Makefile如下:
VPATH = ./source
INC = -I./include
CFLAGS := -O0 -g $(INC)
SOURCE = $(wildcard ./source/*.cpp)
CPPFILES = $(notdir $(SOURCE))
OBJ = $(patsubst %.cpp, %.o, $(CPPFILES))
#OBJ = $(patsubst %.cpp, %.o, $(SOURCE))
EXE = main
#PRINT:
# @echo "CPPFILES:"
# @echo $(CPPFILES)
$(EXE):$(OBJ)
g++ -std=c++11 -o $@ $^
%.o:%.cpp
g++ -std=c++11 -o $@ -c $^ $(CFLAGS)
.PHONY:clean
clean:
-rm $(EXE) *.o
執行make,結果如下:
並且,編譯生成的.o文件也都生成在了當前目錄下:
解決方法3 使用vpath
vpath比VPATH更爲靈活,它可以爲不同類型的文件指定不同的搜索目錄。使用方法:
vpath PATTERN DIRECTORIES
爲符合"PATTERN"的文件指定搜索路徑“DIRECTORIES”,多個路徑可以使用空格或者冒號":"分開。
如 vpath %.c src1 src2 或 vpath %.h inc1:inc2
使用vpath的Makefile文件如下:
vpath %.cpp ./source
INC = -I./include
CFLAGS := -O0 -g $(INC)
SOURCE = $(wildcard ./source/*.cpp)
CPPFILES = $(notdir $(SOURCE))
OBJ = $(patsubst %.cpp, %.o, $(CPPFILES))
#OBJ = $(patsubst %.cpp, %.o, $(SOURCE))
EXE = main
#PRINT:
# @echo "CPPFILES:"
# @echo $(CPPFILES)
$(EXE):$(OBJ)
g++ -std=c++11 -o $@ $^
%.o:%.cpp
g++ -std=c++11 -o $@ -c $^ $(CFLAGS)
.PHONY:clean
clean:
-rm $(EXE) *.o
編譯結果與VPATH結果一致。