2015-06-16 27 views
1

我想寫一個Makefile我的項目,所有的*.c*.h文件是一個名爲src文件夾中,和Makefile文件看起來是這樣的 -的Makefile:通配符和patsubst不改變文件源名稱

CC    := gcc 
CFLAGS   := -g -Wall -ansi -pedantic -std=gnu99 
LDFLAGS   := -lm 
INCLUDES  := $(wildcard src/*.h) 
IFLAGS   := $(addprefix -I/,$(INCLUDES)) 
SRC    := $(wildcard src/*.c) 
OBJS   := $(patsubst %.c, %.o, $(SRC)) 
APP    := app 

all: $(OBJS) 

$(APP): $(OBJS) 
    $(CC) $(CFLAGS) $< -o [email protected] $(LDFLAGS) 

$(OBJS): $(SRC) $(INCLUDES) 
    $(CC) $(CFLAGS) $(IFLAGS) -c $< -o [email protected] 

clean: 
    rm -rf $(OBJS) 
    rm -rf *.out 
    rm -f $(APP) 

在這一點上我不生成可執行,只是想將它們編譯到目標文件,所以當我跑,我得到這個輸出 -

gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/allocate.o 
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/auxiliary.o 
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/decode.o 
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/display.o 

你可以看到,在每個gcc調用,如此urce文件名不會改變,它們全部總是src/allocate.c爲什麼?但是,對象名正確地展開,如src/allocate.o,src/auxiliary.osrc/decode.o等。

+0

目標'clean'和'all'是假目標,因爲不會生成具有這些名稱的文件。因此,在這些目標的第一個之前需要有一行:'.PHONY:clean all' – user3629249

回答

3

看起來你已經在這裏混淆了一些東西。

他們基本上有兩種類型,你需要在這裏使用的規則,他們都共享相同的語法:當你寫這個

targets : prerequisites 
    recipe 

$(APP): $(OBJS) 
    $(CC) $(CFLAGS) $< -o [email protected] $(LDFLAGS) 

你說做您要創建$(APP),並要做到這一點,您需要$(OBJS)存在或將被創建。

現在,當你這樣寫:

$(OBJS): $(SRC) $(INCLUDES) 
    $(CC) $(CFLAGS) $(IFLAGS) -c $< -o [email protected] 

你告訴讓你想創建的.o文件的列表,併爲您所需要的所有$(SRC)$(INCLUDES)每個單獨的文件。

因爲在使用$<的配方中,這是的先決條目列表中的第一個條目的快捷方式,所以最終會生成相同的源文件。


做你想做什麼,你必須抽象的東西一點點,告訴make「這是我多麼希望你建立任何.o文件取決於相應.c」。這是模式規則的工作:

%.o: %.c 
    $(CC) $(CPPFLAGS) $(CFLAGS) -o [email protected] -c $< 

最終,你的Makefile應該是這樣的:

APP    := app 
SRC    := $(wildcard src/*.c) 
OBJ    := $(SRC:.c=.o) 
CFLAGS   := -W -Wall -g -std=c99 -pedantic 
LDLIBS   := -lm 

all: $(OBJS) 

$(APP): $(OBJ) 
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o [email protected] 

clean: 
    $(RM) $(APP) $(OBJ) 

注意另一對夫婦的東西在這裏,你錯過了:

  • -I預處理器標誌(應該放置在CPPFLAGS變量中)接受di教區,而不是一個文件。

  • -ansi編譯器標誌是-std=c89的同義詞。你之後使用-std=gnu99,以便最終挑選出一個

  • 根本不需要列出頭文件。不要打擾。

  • 不要輕易使用rm命令的-r標誌,您將最終刪除文件夾。它不是用來刪除多個文件,而是遞歸刪除,讀取你的男人。

  • 在鏈接階段,您使用$<而不是$^,因此您的可執行文件將會丟失許多目標文件。


爲了解決意見:

GNU做有很多預定義的規則,函數和變量,你應該使用您自己之前使用。它具有編譯和鏈接C和C++程序的基本規則,這就是爲什麼你的Makefile不需要重新定義已存在的%.o: %c規則。

您可以在自己喜歡的shell中鍵入此看到所有這些:

$ make -p > predefined.mk 

$(RM)$(CC)是這些預定義變量之一,你可以自己看看他們實際上包含。

現在,儘可能多的用戶關心頭文件依賴關係,讓我們來解決這個問題。您不必手動執行此操作,現在的GCC和Clang編譯器會在您設置完成後爲您執行此操作。

每個.c文件的依賴關係將在必須包含在Makefile中的.d文件中生成。

要告訴編譯器生成這些文件在編譯時,你需要通過一個預處理標誌:

CPPFLAGS := -MMD 

現在的依賴關係是自動生成的,我們需要將它們包括:

DEP := $(OBJ:.o=.d) 

-include $(DEP) 

你也想清理它們:

clean: 
    $(RM) $(APP) $(OBJ) $(DEP) 

現在你的Makefile看起來像t他:

APP    := app 
SRC    := $(wildcard src/*.c) 
OBJ    := $(SRC:.c=.o) 
DEP    := $(OBJ:.o=.d) 
CPPFLAGS  := -MMD 
CFLAGS   := -W -Wall -g -std=c99 -pedantic 
LDLIBS   := -lm 

all: $(OBJS) 

$(APP): $(OBJ) 
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o [email protected] 

clean: 
    $(RM) $(APP) $(OBJ) $(DEP) 

-include $(DEP) 

末點:語法$(SRC:.c=.o)$(SRC:%.c=%.o)一個快捷方式這也是$(patsubst %.c,%.o,$(SRC))的捷徑。

+1

如果修改了相關頭文件,此解決方案不會重建目標文件。爲此,在你的CFLAGS中加上'-MP'和'-MD',以及在主makefile中加入'-include $(SRC:%。c =%.d)'行(參見http:// www。 microhowto.info/howto/automatically_generate_makefile_dependencies.html查看詳細信息) – John

+0

考慮到OP的實際知識,我選擇不談論這一點,因爲它不是問題的真正組成部分。作爲一個附註,應該使用'-MMD'而不是'-MD'。 – Chnossos

+0

感謝您的詳細回答,我也有一些問題,1.爲什麼沒有規則在您的make中創建'$(OBJ)'?它是從'OBJ:= $(SRC:.c = .o)'自動推斷的嗎? 2.什麼是「$(RM)」? 3. $(通配符src/*。c),$(patsubst%c,%o,$(SRC))'和'$(SRC:.c = .o)'不同? – ramgorur