通用makefile

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經我編譯運行過, 可以成功運行,編譯環境也在開始處列出,如果有問題,歡迎指正,如果有更好的寫法,請給我留言,希望此模板對大家有所幫助

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章