makefile是編譯大型C/C++項目的重要工具,雖說現在有cmake等自動編譯工具,但是,掌握makefile的寫法,依然是C/C++程序開發者必備的技能.
現以一個完整的例子來說明用makefile生成靜態庫(.a文件)以及可執行文件.
編譯環境:
操作系統版本: Debian 3.2.54-2 x86_64
GCC版本: gcc version 4.7.2
目錄及文件組織如下
add.h
add.cpp
minus.h
minus.cpp
main.cpp
Makefile
utils/utils.h
utils/utils.cpp
utils/Makefile
包含當前目錄及一個子目錄utils, 其中,utils下面的文件將被編譯爲一個靜態庫libztx.a
各文件內容如下
$ cat add.h
#ifndef ADD_H
#define ADD_H
#include <stdio.h>
int add(int a, int b);
int add_1();
int add_2();
#endif
$ cat add.cpp
#include "add.h"
int add(int a, int b)
{
return a+b;
}
$ cat minus.h
#ifndef MINUS_H
#define MINUS_H
#include <stdio.h>
int minus(int a, int b);
#endif
$ cat minus.cpp
#include "minus.h"
int minus(int a, int b)
{
return a-b;
}
$ cat main.cpp
#include <stdio.h>
#include <stdint.h>
#include "add.h"
#include "utils.h"
int main()
{
uint32_t now = Utils::now();
uint32_t now1 = time(NULL);
int sum = add(1, 2);
printf("hellow makefile, time:%u, now:%u, sum:%d\n", now1, now, sum);
return 0;
}
$ cat utils/utils.h
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
#include <string>
class Utils
{
public:
static uint32_t now();
static uint32_t to_number(std::string str);
};
#endif
$ cat utils/utils.cpp
#include <time.h>
#include <stdlib.h>
#include "utils.h"
uint32_t Utils::now()
{
return time(NULL);
}
uint32_t Utils::to_number(std::string str)
{
return atoi(str.c_str());
}
main函數在當前目錄的main.cpp文件中
再看看當前目錄和子目錄下的兩個makefile
當前目錄下的Makefile
$ cat Makefile
CC=g++
RM=rm
EXE=hello
SRC_DIR=./
OBJ_DIR=.objs
DEP_DIR=.objs
INC_DIR=-I./ -I./utils
CCFLAGS= -g -Wall
## refer to wplan
SRC=$(wildcard *.cpp)
OBJS=$(addprefix $(OBJ_DIR)/, $(patsubst %.cpp, %.o, $(notdir $(SRC))))
DEPS=$(addprefix $(DEP_DIR)/, $(patsubst %.cpp, %.d, $(notdir $(SRC))))
LIBS = -L./utils -lztx -lpthread -lmysqlclient ## 如果用不到線程庫和mysql庫,這兩個庫可以不用鏈接進來
all: $(EXE)
${EXE}:${OBJS} ./utils/libztx.a
@##$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
@## 因爲生成EXE需要所有.o文件參與所以下面的指令使用$^, 而不應該用第一個依賴$<
@echo "object files: ${OBJS}"
${CC} -g -Wall -o $@ $^ $(LIBS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CC) -c -g -Wall $< -o $@ $(INC_DIR)
$(DEP_DIR)/%.d:$(SRC_DIR)/%.cpp
@set -e; rm -f $@; \
$(CC) -MM $(INC_DIR) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,$(OBJ_DIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
-include $(DEPS) ## include all dep files in the makefile
## rule to generate a dep file by using C preprocessor
## see man cpp for details on the -MM and -MT options
.PHONY: clean
clean:
${RM} -f ${EXE} ${OBJS} ${DEPS}
## next is annotation for this Makefile
## $@: 目標文件名稱
## $^: 所有的依賴文件,以空格分開,不包含重複的依賴文件
## $<: 第一個依賴文件的名稱
## patsubst: 替換通配符, 把變量$(SRCS)中符合後綴是.cpp的文件全部替換成.o
## end of Makefile
當前目錄下的Makefile用來生成可執行文件hello, 要引用到子目錄utils下面的庫,用來獲得當前時間
也就是main函數裏面的 uint32_t now = Utils::now(); 這行需要用到utils下面的靜態庫
再看看子目錄utils目錄下面的Makefile
$ cat utils/Makefile
CC=g++
RM=rm
TARGET=libztx.a
SRC_DIR=./
INC_DIR=-I./
OBJ_DIR=../.objs
DEP_DIR=../.objs
AR=ar
CPPFLAGS = -g -Wall
SRC=$(wildcard *.cpp)
OBJS=$(addprefix $(OBJ_DIR)/, $(patsubst %.cpp, %.o, $(notdir $(SRC))))
DEPS=$(addprefix $(DEP_DIR)/, $(patsubst %.cpp, %.d, $(notdir $(SRC))))
all: $(TARGET)
$(TARGET):$(OBJS)
@echo "\n\n"
@echo "first rely file" $<
$(AR) -cr $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CC) -c -g -Wall $(INC_DIR) $< -o $@
$(DEP_DIR)/%.d : $(SRC_DIR)/%.cpp
@set -e; rm -f $@; \
$(CC) -MM $(INC_DIR) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,$(OBJ_DIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sinclude $(DEPS)
.PHONY: clean
clean:
$(RM) -f $(TARGET) $(OBJS) $(DEPS)
## end of Makefile in utils
編譯方法:
進入子目錄, 運行utils/Makefile, 生成靜態庫 libztx.a
$ cd utils
$ make
此時可以看到utils目錄下面生成了libztx.a
在進入當前目錄,運行Makefile, 生成可執行文件
$ cd ..
$ make
可以看到當前目錄生成了可執行文件hello
運行可執行文件,得到結果
$ ./hello
hellow makefile, time:1545631453, now:1545631453, sum:3
以上就是整個工程的代碼文件和makefile文件,在指定的編譯環境下可運行,也可以作爲大型工程的模板.
該makefile模板有如下功能:
1. 當頭文件改變後(即.h文件), 再次執行make命令,會導致相關的源文件重新編譯, 比如在add.h中加一個空行(即敲一個回車), 則add.cpp main.cpp兩個文件會重新編譯, 然後重新生成可執行文件
2. 修改某個源文件(即.cpp文件)時,再次執行make命令,只有這個文件重新編譯,然後重新生成可執行文件,這樣就大大節省了編譯時間
3. 可以鏈接自定義的靜態庫(上面的utils目錄下的libztx.a)並使用靜態庫中的函數(Utils::now()函數)
以上makefile經我編譯運行過, 可以成功運行,編譯環境也在開始處列出,如果有問題,歡迎指正,如果有更好的寫法,請給我留言,希望此模板對大家有所幫助