我們習慣創建一個環境變量文件Inc.mk來定義常用的變量
CC = gcc
CXX = g++ -std=c++11
AR ?= ar
ARFLAGS = -scurv
RANLIB ?= ranlib
CFLAGS ?=
CXXFLAGS ?=
INCLUDE ?=
LDFLAGS ?=
CFLAGS += -Wall -g -fPIC -Wl,-z -Wl,defs -DDEBUG
CXXFLAGS += -Wall -g -fPIC -Wl,-z -Wl,defs -DDEBUG
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(INCLUDE) -c $<
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDE) -c $<
ar命令:
-s 創建歸檔索引(等同ranlib)
-c 如果必須創建庫,請不要發出警告
-u 僅替換比當前存檔內容更新的文件
-r 替換現有文件或將新文件插入存檔
-v 詳細說明
ar -s其實等同於ranlib,由於早期linux系統ar只是單純打包多個.o文件到.a靜態庫,而不處理.o的符號表,所以需要ranlib來生成鏈接所需要的符號信息。現在其實不需要第二步ranlib。
編譯選項:
-w 關閉所有警告信息
-Wall 提供有用的警告信息
-Werror 警告信息變成錯誤,必須處理
-g 產生調試信息,給調試器用,可以用-g3這個級別可以調試宏;如果調試器是gdb,建議使用-ggdb或者-ggdb3
-fPIC 是生成動態庫使用的,編譯器就輸出位置無關目標碼,適用於動態連接,即使分支需要大範圍轉移
-Wl,-z -Wl,defs 鏈接編輯器生成共享庫輸出文件時,允許在鏈接編輯結束時仍存在未定義符號。此缺省行爲允許共享庫從將其定義爲依賴性的動態可執行文件導入符號。-z defs選項強制在存在任何未定義符號的情況下生成致命錯誤
-DDEBUG 相當於定義了宏DEBUG,代碼中可以用#ifdef DEBUG來控制調試輸出信息等
現在來看下正文Makefile文件
include ../Inc.mk
DIR_MODULES=crypto \
net
SRC := $(wildcard *.cpp)
OBJS := $(patsubst %.cpp, %.o, $(SRC))
SO_NAME = libcomm.so
SO_BIN = $(LIB_DIR)/$(SO_NAME)
A_NAME = libcomm.a
A_BIN = $(LIB_DIR)/$(A_NAME)
INCLUDE += -I$(XML_INC)
LDFLAGS += -L$(LIB_DIR) -ltinyxml2
all : clean $(SO_BIN) $(A_BIN)
@for dir in $(DIR_MODULES); \
do \
make -C $$dir; \
echo; \
done
$(SO_BIN) : $(OBJS)
$(CXX) $(CXXFLAGS) -shared -Wl,-soname,$(SO_NAME) -o $@ $^ $(LDFLAGS)
$(A_BIN) : $(OBJS)
$(AR) $(ARFLAGS) $@ $?
$(RANLIB) $@
clean :
@for dir in $(DIR_MODULES); \
do \
make -C $$dir clean; \
echo; \
done
rm -rf $(OBJS) $(SO_BIN) $(A_BIN)
靜態庫的生成一般是兩步:
ar -scurv libcomm.a *.o
ranlib libcomm.a
這是早期linux做法,現在可以省去ranlib這一步
動態庫的生成需要用到-shared
-Wl,-soname 是爲了提高新老版本庫的兼容性,通過soname指定我們所希望的庫版本
include ../Inc.mk
SRC := $(wildcard *.cpp)
OBJS := $(patsubst %.cpp, %.o, $(SRC))
BIN = $(BIN_DIR)/server
INCLUDE += -I$(COMM_INC) -I$(XML_INC)
LDFLAGS += -L$(LIB_DIR) -ltinyxml2 -lcomm
all : clean $(BIN)
$(BIN) : $(OBJS)
$(CXX) $(CXXFLAGS) -Wl,-rpath=$(LIB_DIR) -o $@ $^ $(LDFLAGS)
clean :
rm -rf $(OBJS) $(BIN)
-Wl,-rpath 是爲了指定我們鏈接的庫路徑,就不需要再去修改LD_LIBRARY_PATH變量了
這裏我們看到每個Makefile文件都include ../Inc.mk
1. 在Inc.mk中,我們定義了固定的幾個參數,gcc,g++還有編譯選項等,在Makefile中include之後就可以直接使用,但是INCLUDE和LDFLAGS我們並沒有固定,而是在Makefile中自定義,因爲它們要引用的頭文件,庫文件都是各取所需。
2. 在Inc.mk中,我們定義了.c生成.o和.cpp生成.o的規則,在Makefile中include之後就可以直接使用了,所以我們在Makefile中只需要寫.o生成可執行文件的規則就行了,生成可執行文件需要包含的頭文件INCLUDE和需要鏈接的庫LDFLAGS我們自定義,就配合完美了。