[時間:2017-01] [狀態:Self]
[關鍵詞:makefile,gcc,編譯,動態庫,靜態庫,可執行文件,shell命令]
引言
前段時間在Linux下編寫一個可測試的程序發現,我對makefile實踐太少各種別扭吧。因此參考網上的一篇文章,自己嘗試下常用的可執行文件、靜態庫、動態庫的自動編譯,以及調用,作為后續開發的參考。
本文主要包括三部分:
- 可執行文件的Makefile
- 靜態庫*.a的Makefile
- 動態庫*.b的Makefile
這是我對Makefile學習總結的第二篇文章,其他的可參考GNU make簡介。
本文主要參考,感謝@竹林聽雨的分享。
在Linux下編譯時常用的命令有:
- make : 編譯整個工程
- make install : 輸出編譯之后的結果,可能是系統目錄,也可能是自定義目錄
- make clean : 清除編譯過程中的中間文件,比如.o,.s等
- make distclean : 恢復編譯前的環境,注意本文中使用make veryclean
1. 可執行文件的Makefile
代碼如下:
#源文件,自動找所有.c和.cpp文件,並將目標定義為同名.o文件
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))
#目標文件名,輸入任意你想要的執行文件名
TARGET := exe_check
#編譯參數
CC := g++
LIBS :=
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
#下面的基本上不需要做任何改動了
.PHONY : everything objs clean veryclean rebuild
everything : $(TARGET)
all : $(TARGET)
objs : $(OBJS)
rebuild: veryclean everything
clean :
rm -fr *.so
rm -fr *.o
veryclean : clean
rm -fr $(TARGET)
#這里是實際完成編譯的命令
$(TARGET) : $(OBJS)
$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
如果你是直接拷貝這個makefile的話,麻煩檢查下上面的shell命令前空白必須是Tab鍵,不能是空格。
2. 靜態庫的Makefile和調用示例
Makefile文件如下,主要使用ar/ranlib打包命令:
#共享庫文件名,lib*.a
TARGET := libtest.a
#編譯參數
CC := g++
AR = ar
RANLIB = ranlib
LIBS :=
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
#下面的基本上不需要做任何改動了
#源文件,自動找所有.c和.cpp文件,並將目標定義為同名.o文件
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))
#頭文件,自動查找.h文件
HEADER := $(wildcard *.h)
.PHONY : everything objs clean veryclean rebuild install
everything : $(TARGET)
all : $(TARGET)
objs : $(OBJS)
rebuild: veryclean everything
clean :
rm -fr *.o
veryclean : clean
rm -fr $(TARGET)
install :
test -f ./sample/lib && echo ' ' || mkdir ./sample/lib
cp $(TARGET) ./sample/lib/
test -f ./sample/include/ && echo ' ' || mkdir ./sample/include
cp $(HEADER) ./sample/include/
$(TARGET) : $(OBJS)
$(AR) cru $(TARGET) $(OBJS)
$(RANLIB) $(TARGET)
由於一般動態庫或者靜態庫需要安裝,這里添見了install標簽。
調用靜態庫的話,需要使用下面makefile,這里僅給出相對可執行文件的makefile差別,如下:
#compile and lib parameter
#編譯參數
CC := g++
LIBS := -ltest
LDFLAGS := -L./lib/
DEFINES :=
INCLUDE := -I. -I./include
CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
主要加入了靜態庫所在的目錄及-l命令。
3. 動態庫的Makefile及調用示例
Makefile如下,直接調用g++編譯成動態庫:
#共享庫文件名,lib*.so
TARGET := libtest.so
#編譯參數
CC := g++
LIBS :=
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
SHARE := -fpic -shared -o
#下面的基本上不需要做任何改動了
#源文件,自動找所有.c和.cpp文件,並將目標定義為同名.o文件
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))
#頭文件,自動查找.h文件
HEADER := $(wildcard *.h)
.PHONY : everything objs clean veryclean rebuild install
everything : $(TARGET)
all : $(TARGET)
objs : $(OBJS)
$(CC) -c -fPIC $(SOURCE)
rebuild: veryclean everything
clean :
rm -fr *.o
veryclean : clean
rm -fr $(TARGET)
install :
test -d ./sample/lib && echo ' ' || mkdir ./sample/lib
cp $(TARGET) ./sample/lib/
test -d ./sample/include/ && echo ' ' || mkdir ./sample/include
cp $(HEADER) ./sample/include/
$(TARGET) : objs
$(CC) $(CXXFLAGS) $(SHARE) $@ $(OBJS) $(LDFLAGS) $(LIBS)
這里就是需要關注下-fPIC和-fpic的不同使用位置,否則可能會出現編譯錯誤。
調用動態庫的示例代碼的Makefile跟可執行文件的主要區別如下:
#目標文件名,輸入任意你想要的執行文件名
TARGET := shared_check
#編譯參數
CC := g++
LIBS := -ltest
LDFLAGS := -L./lib/
DEFINES :=
INCLUDE := -I. -I./include
CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
4. 小結
簡單整理完了,直接看Makefile確實不是很難,但是其中涉及比較多的shell命令,比如wild、test、patsubst以及gcc編譯指令,這方面有待加強。
不做實際驗證才不會明白其中有各種坑的。簡單過一遍GNU Makefile還是很有幫助的。
本文所有涉及的Makefile及源碼可以從我的git下載,url:https://git.oschina.net/Tocy/SampleCode.git。相關文件位於makefile-template文件夾下。
