2013-04-18 57 views
1

我在這個網站上尋找解決方案,也嘗試谷歌一段時間了,但不知何故,我不能讓它工作。Makefile:沒有規則,使目標

我的源代碼應該在src目錄中,而目標文件應該在obj目錄中。現在我嘗試創建一個簡單的makefie,但是我得到一個沒有規則的錯誤,或者我無法使它工作來使用目錄。

CC = /usr/bin/gcc 
CXXFLAGS = -O2 -g -Wall -fmessage-length=0 

SRC:=  nohupshd.cpp \ 
      task.cpp 

OBJ:=  nohupshd.o \ 
      task.o 

OBJDIR:=  obj 
SRCDIR:=  src 

DEP:=  src/task.h 
LIBS:= 

TARGET:= nohupshd 

all: $(TARGET) 

$(TARGET): $(OBJ) 
    $(CC) -o $(TARGET) $(OBJ) $(LIBS) 

clean: 
    rm -f $(OBJ) $(TARGET) 

變體1:

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp 
    $(CC) -S $(SRCDIR)/$< -o $(OBJDIR)/[email protected] 
    $(CC) -c $(SRCDIR)/$< -o $(OBJDIR)/[email protected] 

變1A:

%.o: %.cpp 
    $(CC) -S $(SRCDIR)/$< -o $(OBJDIR)/[email protected] 
    $(CC) -c $(SRCDIR)/$< -o $(OBJDIR)/[email protected] 

當我使用這個模式,我總是得到一個錯誤,沒有規則nohupshd.o建設。

變2:

$(OBJ) : $(OBJDIR)/%.o: $(SRCDIR)/%.cpp 
    $(CC) -S $(SRCDIR)/$< -o $(OBJDIR)/[email protected] 
    $(CC) -c $(SRCDIR)/$< -o $(OBJDIR)/[email protected] 

當我使用這個變體,我可以看到,它試圖建立,但我得到的錯誤說,「文件」的.o不適合的目標模式。

另一個問題是,「$ <」不給我源名稱。根據幾個網站它應該,但我可以看到在輸出中沒有什麼,所以我該如何解決這個問題?

更新:

在此期間我的最新版本是這樣的:

$(OBJDIR)/$(OBJ) : $(OBJDIR)/%.o : $(SRCDIR)/%.cpp 
    $(CC) -S $< -o $(OBJDIR)/`basename [email protected] .o`.asm 
    $(CC) -c $< -o [email protected] 

這現在管理編譯第一objectfile(nohupshd.o),但是當make試圖做第二個文件,它再次失敗說:target'task.o'與模式不匹配。

+0

至少現在我明白了一部分現實問題。目標文件位於obj目錄中。當我把它作爲依賴項$(OBJDIR)/ $(OBJ)時,它會擴展爲obj/o1.o o2.o ...而它應該是obj/o1.o obj/o2.o ...,那麼,我必須在每個單獨的文件上真正指定obj路徑嗎? – Devolus

回答

0

所以最後我發現瞭如何寫這個makefile文件,爲我的錯誤的exaplanation答案看帖子我標記爲正確的答案:

產生的生成文件看起來是這樣的,對於完整性我張貼在這裏,包括頭文件的依賴關係(去掉ASM部分,如果你不需要「時間):

​​

我希望這可以幫助其他用戶。我發現的所有例子都非常簡單,並且單獨列出了多個文件,而不是規則的一部分,但沒有真正解釋它是如何工作的,或者是如此複雜以至於我無法找出它如何幫助我。

1

不要在編譯器行中重複目錄名稱。 $<[email protected]已經有目錄名稱。

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp 
    $(CC) -S $< -o [email protected] 
    $(CC) -c $< -o [email protected] 
+0

是的,我在此期間發現了這一點,但這並不能解決問題的其餘部分。 – Devolus

+2

您必須更改'OBJ',以便目標文件具有'$(OBJDIR)'前綴。你並沒有要求構建'obj/task.o',而只是要求'task.o'被構建。 Make不會猜測,也許你確實是指其他文件。如果你想建立一個文件,那麼目標必須是_that_文件名,完全是。前提條件必須是_that_文件名,完全是。 – MadScientist

+0

是的,經過大量的擺弄之後,我終於找到了makefile的外觀。不幸的是,我發現了很多例子,但都是過於簡單或過於複雜。我很驚訝這樣一個共同的事情是不容易找到一個現實世界的例子。 – Devolus

6

如果上述不正確的事情,你實際上有一對夫婦。

首先你寫我的錯誤是,我假設模式%.o匹配任何以.o結尾的模式,它不是;這不是真的。模式確實與匹配以.o結尾的任何字符串。但是,目標端匹配的模式字符%在前提條件一側被替換爲相同的字符串。所以,如果你有一個目標obj/task.o和它的模式匹配%.o那麼(什麼手動調用它)將是obj/task,當前提是%.c這意味着,化妝會尋找一個先決條件obj/task.c。由於沒有一個,並且不知道如何建立一個,所以該規則被丟棄,因爲不適用。在編寫模式規則時,您必須編寫它們,以便僅限名稱的相同部分與模式字符(%)匹配。必須明確指定所有不相同的部分,包括目錄。

二,規則$(OBJ) : $(SRC)真的不對。該行表示每個目標文件都依賴於所有源文件,因此無論何時任何單個源文件更改all,目標文件都將被重新編譯。這真的不是你想要的(如果這是你想要的,你不需要make:你可以只寫一個簡單的shell腳本)。我不知道你的意思是,因爲規則是空的,它調用模式規則;你不需要這個來調用模式規則。目標取決於$(OBJ),每個目標文件都依賴於其源文件(由於該模式)。你根本不需要這條線。第三,我不知道你爲什麼要構建.asm文件,而不是直接從源代碼編譯爲對象,但是如果你真的想要它們,它會更乾淨,更「像」一樣來創建一個.asm文件。單獨的模式規則來構建它們:創建模式規則$(OBJDIR)/%.o : $(OBJDIR)/%.asm和規則$(OBJDIR)/%.asm : $(SRCDIR)/%.c。如果您希望將ASM文件作爲構建的產品,則應聲明它們爲all或類似的先決條件,否則它們將作爲中間文件被刪除。

第四,使用像basename這樣的東西是不必要的。有很多自動make變量可以用來代替。例如,$*擴展到詞幹,因此您可以編寫$(OBJDIR)/$*.asm。當然,如果您爲ASM文件制定單獨的模式規則,則可以直接使用[email protected]$<。有各種製造功能也可以使用;請參閱手冊。第五,你定義一個包含頭文件DEP的變量,但是不要使用它。因爲它沒有被使用,所以如果你改變那個文件什麼都不會被重建。如果您知道所有源文件都包含每個標題,則可以使用$(OBJ) : $(DEP)來定義該標題;但它確實意味着(如上面第二點),對任何標題的任何更改都會導致所有對象重新編譯。你最好自動生成先決條件;因爲你使用的是GCC,所以這很簡單。

第六,您正在使用C++文件(xxx.cpp),但您使用的是C編譯器。這是行不通的(鏈接將失敗:雖然編譯器可以看到你正在編譯C++文件並做正確的事情,即使你調用gcc,當你連接一堆對象時,也不知道這些是否是C對象或C++對象(或FORTRAN或其他),因此您必須使用C++前端進行鏈接,否則將無法使用正確的C++庫)。您應該使用make變量CXX來構建C++代碼,而不是CC,並將其設置爲g++而不是gcc

第七,你不需要.SUFFIXES: .c .o來使用模式規則。他們只需要後綴規則,這是你沒有的。儘管如此,您仍然可以保留原始的.SUFFIXES:以禁用內建的模式匹配,這只是輕微的性能提升。

最後,你會注意到你實際上並不需要$(SRC)變量,因爲make可以從模式規則中推斷出來。然而,如果你想讓你的makefile變得不那麼麻煩,你可以從SRC變量中構建OBJ變量的內容,如SRC = nohupshd.cpp task.cpp然後OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(SRC))

所以,全在,這就是我會建議你寫你的makefile文件(這裏我就不雖然包括自動生成的依賴):

.SUFFIXES: 

CXX :=  g++ 
CXXFLAGS := -O2 -g -Wall -fmessage-length=0 

OBJDIR := obj 
SRCDIR := src 

TARGET := nohupshd 
SRC :=  nohupshd.cpp task.cpp 
DEP :=  src/task.h 
LIBS := 

# ---- 

OBJ :=  $(patsubst %.cpp,$(OBJDIR)/%.o,$(SRC)) 
ASM :=  $(patsubst %.cpp,$(OBJDIR)/%.asm,$(SRC)) 

.PHONY: all clean 

all: $(TARGET) $(ASM) 

$(TARGET): $(OBJ) 
     $(CXX) -o [email protected] $^ $(LIBS) 

clean: 
     rm -f $(OBJDIR)/* $(TARGET) 

$(OBJDIR)/%.o : $(SRCDIR)/%.asm 
     $(CXX) $(CXXFLAGS) -c -x assembler-with-cpp $< -o [email protected] 

$(OBJDIR)/%.asm : $(SRCDIR)/%.cpp 
     $(CXX) $(CPPFLAGS) -S $< -o [email protected] 
+0

感謝您對模式的解釋。這真的很有幫助。在你的例子中有一個小錯誤,因爲你在模式替換「* .c」中寫入而不是「* .cpp」,但這是一個小問題。我保留原樣,因爲我不想爲此制定單獨的規則。我更喜歡生成asm輸出,所以我可以檢查編譯器真的在做什麼。 :)大多數用戶可以簡單地刪除它。如果我要創建一個單獨的規則,我將不得不編譯爲asm來以單獨的步驟進行反對,這在這裏沒有意義。我現在更新我的上面發佈的工作版本。謝謝! – Devolus

+0

當然,你喜歡它。你的解決方案的問題是你大大增加了編譯時間,因爲你運行了大部分編譯器工作兩次。一旦程序集可用,將其轉換爲目標文件幾乎沒有任何工作:大部分工作是在預處理,解析,尤其是優化來生成程序集。如果你想彙編輸出最有效的方法是首先生成彙編,然後編譯_assembly_到目標文件中。 – MadScientist