編寫Makefile是一個苦樂交織的事情,快樂是因為從一堆需要手工逐個處理的編譯過程,進步到一條命令完成,看着代碼順暢的在屏幕上滾動,編譯為最終的產品,那個過程無比愉悅;而痛苦則是,寫代碼已經很累了,寫完代碼還要編寫Makefile,這多出來的一點工作,很有點最后一根稻草的感覺。
最近整理手頭的幾個項目,把C語言類的Makefile抽象、合並了一下,形成了一個比較通用的編譯腳本,這里分享一下:
#定義編譯器
CC=gcc
#自己特定的編譯參數,這里僅為示例,這個參數是消除mac編譯openssl類程序用的
CFLAGS += -Wno-deprecated-declarations
#定義輸出文件夾,outs默認等於是./outs
OUTSDIR = outs
#定義.o中間文件的路徑,這個路徑編譯完成可以清除
TMPSDIR = objs
#源代碼路徑
VPATH=src
#掃描所有的c源碼,這里默認src中所有文件都是相當於庫文件,最終編譯為.o
#搜索出來的文件包含了src路徑,這里也去掉,便於后面編譯到臨時目錄
OBJSSOURCE = $(notdir $(wildcard src/*.c))
OBJS = $(patsubst %.c,%.o,$(OBJSSOURCE))
#主程序名
KEYS = main
DEPS = $(addprefix $(TMPSDIR)/,$(OBJS))
.PHONY : all
all:$(OBJS) $(KEYS)
#編譯所有的庫文件由.c至.o
#因為VPATH的存在,源文件會自動檢索src目錄
$(filter %.o,$(OBJS)) : %.o : %.c
$(CC) $(CFLAGS) -c -o $(TMPSDIR)/$@ $<
#利用所有的庫文件編譯主程序
$(KEYS): $(DEPS)
$(CC) $(CFLAGS) -o $(OUTSDIR)/$(notdir $@) $(notdir $@).c $(DEPS)
#清理
.PHONY : clean
clean:
-rm $(OUTSDIR)/* $(TMPSDIR)/*
這個編譯腳本的主要特點是自動掃描所有的源文件,然后逐個編譯,對於大多c類的項目,基本只需要定義一下主程序就可以完成編譯了,其實根據同樣的原理連主程序都一起掃描、編譯也是可以的,只是似乎自由度太差了。
腳本簡單修改可以適應各種環境,比如下面再貼一個ios使用的,ios如果非越獄的話,直接編譯成可執行文件是沒有意義的,這里我們假設編譯成.a庫文件,供xcode來調用:
#ios交叉編譯器
CC=$(shell xcrun --sdk iphoneos --find clang)
_SFLAG=$(shell xcrun --sdk iphoneos --show-sdk-path)
#iphone6以后都是arm64了,所以這里不再考慮armv7,另外也不考慮模擬器運行了
#如果有需要可以根據自己的需求修改
CFLAGS += -Wno-deprecated-declarations -isysroot $(_SFLAG) -arch arm64 -mios-version-min=9.3 -fembed-bitcode
OUTSDIR = outs
TMPSDIR = objs
VPATH=src
OBJSSOURCE = $(notdir $(wildcard src/*.c))
OBJS = $(patsubst %.c,%.o,$(OBJSSOURCE))
DEPS = $(addprefix $(TMPSDIR)/,$(OBJS))
#最后生成的庫
KEYS = libcallfunctions.a
.PHONY : all
all:$(OBJS) $(KEYS)
#編譯所有的庫文件由.c至.o
$(filter %.o,$(OBJS)) : %.o : %.c
$(CC) $(CFLAGS) -c -o $(TMPSDIR)/$@ $<
#將.o文件打包為庫
libcallfunctions.a : $(DEPS)
ar -r $(OUTSDIR)/libcallfunctions.a $(DEPS)
#清理
.PHONY : clean
clean:
-rm $(OUTSDIR)/* $(TMPSDIR)/*
在主要的編譯環節,還有下面這種常用的辦法,只是自己運算得到了源文件名而沒有用Make系統的自動搜索功能而已:
%.o:
$(CC) $(CFLAGS) -c -o $(TMPSDIR)/$@ src/$(patsubst %.o,%.c,$@)
對於更復雜的編譯模式,建議把每個編譯環節定義成子程序來執行,可以具備更多的靈活性。另外當前這個腳本有一個bug就是每次編譯實際上所有的.o文件都會完整重新編譯一遍,而沒有判斷源文件是否更新並忽略沒有更新的源文件,所以不適合大的系統。