2013-03-11 318 views
5

簡而言之:我想從不同目錄編譯源代碼,並將目標文件放入當前目錄。在makefile中編寫編譯規則時如​​何去掉源文件的路徑?

例如,我有文件:

test.c 
../../lib1/boot.c 
../../lib2/startup.c 
../common/utils.c 

(也幾個文件.S(組裝)和.cpp,但我希望這並不重要)。

他們的所有對象的文件我想在當前目錄:

test.o 
boot.o 
startup.o 
utils.o 

我無法弄清楚如何寫在我的makefile這樣的規則。

例如,

%o.: %.c 

因爲make無法找到一個規則來建立從../../lib1/boot.cboot.o現在無法正常工作,只能查找規則從../../lib1/boot.c建立../../lib1/boot.o

我試圖用這樣的:

%o.: %.c 
(my compilation line, for example "gcc -c $^ -o [email protected]") 
%o.: ../../lib1/%.c 
(my compilation line) 
%o.: ../../lib2/%.c 
(my compilation line) 
%o.: ../common/%.c 
(my compilation line) 

和它的作品。但顯然這不夠通用,另外,有些用戶今天來到我這裏說他的應用程序也有一些../../some_other_lib/common_things.c,因此我的makefile失敗了。我瀏覽了我們的項目,發現很多這樣的案例涉及很多不同的目錄。用我的方法,我必須爲每個這樣的目錄編寫一個單獨的規則,並使用相同的編譯行。這對我來說並不好。

所以我的問題是:如何使一些通用的編譯規則放入(和檢查)目標文件在當前目錄中,同時在不同目錄中操作源代碼?

謝謝。

+0

你有沒有考慮過使用'VPATH'? – Idelic 2013-03-11 19:36:51

+0

Idelic,不幸的是,不,我沒有嘗試過。另請參閱這裏的eriktous的答案。 – AlexL 2013-03-12 18:48:23

回答

1

OK,我花了一些時間,但最後我發現了(順便說一下使用一些線程在這個網站)解決方案:

# Defining compilation rules in a way that object files will be produced in current directory, and not in the directory of source files: 
all: <List of my targets> 

define my_c_rule 
$(subst .c,.o,$(notdir $(1))): $(1) 
    $(CC) $(CFLAGS) $(CDEFINES) $$^ -o [email protected] 
endef 
$(foreach f, $(CSRC), $(eval $(call my_c_rule, $(f)))) 

$(CSRC)包含其路徑的源文件的列表。

只需要考慮到,如果早先我有這樣的事情:

.c.o: 
    $(CC) $(CFLAGS) $(CDEFINES) $^ -o [email protected] 

all: <List of my targets> 

...現在我不得不把all上面的句子,我在my_c_rule過程中描述的規則。如果我不這樣做,make在編譯第一個源文件後停止。這是因爲舊的「通配符」規則(例如.c.o%.o: %.c)不會將all替換爲默認目標(即使是較早寫入),但非通配符規則(如上述宏的結果boot.o: ../../lib1/boot.c)會替換默認目標早先寫的。

1

嘗試使用生成文件的功能notdir就象這樣:

%.o: %.c 
    gcc -c $< -o $(notdir [email protected]) 

[email protected]必須等於完整路徑例如:../../lib2/startup.o廣告notdir將它樹幹:startup.o

使用此規則,您將能夠編譯當前目錄中的所有源代碼。

其實,你的例子是這樣的:

. 
└── common 
    ├── lib1 
    │   └── boot.c 
    ├── lib2 
    │   └── startup.c 
    ├── test 
    │   ├── Makefile 
    │   └── test.c 
    └── utils.c 

我想我會是這樣的美好:

. 
├── common 
│   ├── lib1 
│   │   ├── Makefile 
│   │   ├── obj 
│   │   └── src 
│   │    └── boot.c 
│   ├── lib2 
│   │   ├── Makefile 
│   │   ├── obj 
│   │   └── src 
│   │    └── startup.c 
│   ├── Makefile 
│   ├── obj 
│   ├── src 
│   │   └── utils.c 
│   └── test 
│    ├── Makefile 
│    ├── obj 
│    └── src 
│     └── test.c 
└── Makefile 

爲此你需要所有的Makefile文件調用子目錄Makefile文件。 和src/obj dirs是源和對象之間的分隔。

SRC := utils.c 

OBJ := $(SRC:%.c=%.o) 

NAME := project 

SRC_D := src 
OBJ_D := obj 

SUBDIRS := lib1/ \ 
      lib2/ \ 
      test/ 

all: $(NAME) $(SUBDIRS) 
    @for dir in $(SUBDIRS); \ 
    do \ 
    $(MAKE) -C $$dir; \ 
    done 

$(NAME): $(OBJ:%.o=$(OBJ_D)/%.o) 

$(OBJ_D)/%.o : $(SRC_D)/%.c 
    gcc -c $< -o [email protected] 
+0

Sirttas,我知道「notdir」,但我的問題不在編譯行(即「gcc ...」)。這是在規則(這是「%.o:%.c」)。我看不出你的例子如何解決這個問題。如果你解釋我會很感激,因爲如果我理解正確的話,在你的對象文件的回覆結構中會重複源文件的結構;您只需將「src」和「obj」作爲上層目錄添加到所有路徑。 – AlexL 2013-03-11 15:21:49

+0

我的例子是不正確的,你是真的,我想你必須有一個遞歸的makefile編譯'lib1','lib2'和'common'。我將編輯。 – Sirttas 2013-03-11 15:35:08

+0

對不起,我不能遞歸執行它,因爲我不知道我的用戶將使用哪些目錄。我需要它來處理源目錄,其數量,嵌套和名稱是事先未知的。具有完整路徑的源文件列表作爲'make'的參數。 – AlexL 2013-03-11 15:45:27

3

可以從CSRC變量中提取目錄,$(dir ...),然後可以在vpath指令中使用此列表。

vpath %.c $(sort $(dir $(CSRC))) 
vpath %.s $(sort $(dir $(SSRC))) 
vpath %.cpp $(sort $(dir $(CPPSRC))) 

(我甩在了sort函數刪除重複的,但是這不是絕對必要的。)

現在的規則可以保持簡單和make將搜索的目錄列表中的源文件。

$(COBJ) := $(notdir $(CSRC)) 
$(SOBJ) := $(notdir $(SSRC)) 
$(CPPOBJ) := $(notdir $(CPPSRC)) 

.PHONY: all 
all: $(EXECUTABLE) 

$(EXECUTABLE): $(COBJ) $(SOBJ) $(CPPOBJ) 
    .... 

$(COBJ): %.o: %.c 
    ... 

$(SOBJ): %.o: %.s 
    ... 

$(CPPOBJ): %.o: %.cpp 
    ... 
+0

我懷疑這可以通過這種方式完成,但我找不到確切的解決方案,因爲我不瞭解vpath是如何工作的。所以我選擇了另一種解決方但是,謝謝,如果我的解決方案需要一些潛在的未來擴展任務,我會考慮您的解決方案。 – AlexL 2013-03-12 12:42:58

+0

'notdir'正是我一直在尋找的東西。 – phyatt 2016-02-09 16:30:47