2013-10-10 15 views
9

Tôi có tệp makefile được đơn giản hóa sau đây và tôi đang cố gắng đặt các đường dẫn khác nhau dựa trên các mục tiêu khác nhau. Thật không may, tôi không nhận được kết quả mà tôi mong đợi. Điều này là với làm cho phiên bản 3.81.Tại sao makefile này nhắm mục tiêu biến cụ thể không mở rộng như mong đợi?

.SECONDEXPANSION: 
all: Debug32 

# Object directory set by target 
Debug32: OBJDIR = objdir32 
#OBJDIR = wrongdirectory 

# ObjDir is empty here. :(
OBJS = $(addprefix $(OBJDIR)/,DirUtil.o) 

$(OBJDIR)/%.o : %.cpp 
      echo Compile: [email protected] 

Debug32: $(OBJS) 

$(OBJS): | $(OBJDIR) 

$(OBJDIR): 
      echo mkdir $(OBJDIR) - [email protected] 

Kết quả như sau không có thiết lập của OBJDIR:

echo Compile: /DirUtil.o 

Nếu tôi bỏ ghi chú "OBJDIR = wrongdirectory" dòng, tôi sẽ nhận được kết quả như sau, mà đang bối rối kể từ khi tôi nhìn thấy cả hai giá trị của biến nơi tôi nghĩ rằng tôi chỉ cần nhìn thấy một:

echo mkdir objdir32 - wrongdirectory - 
echo Compile: wrongdirectory/DirUtil.o 

tôi giả định rằng các biến không được mở rộng khi tôi nghĩ rằng họ nên, nhưng tôi không thể tìm ra cách để thay đổi Beh này avior.

Trả lời

11

Từ các thông tin GNU thủ

biến và chức năng trong tất cả các bộ phận của một makefile được mở rộng khi đọc, trừ trong những bí kíp, hai bên cánh tay phải của định nghĩa biến sử dụng '=', và các xác định biến số sử dụng chỉ thị 'xác định'.

Biến mục tiêu cụ thể chỉ áp dụng trong công thức nấu ăn. Trong

$(OBJS): | $(OBJDIR) 

$(OBJDIR): 

nó là nhận được biến toàn cầu.

Vì vậy, làm việc qua những gì sẽ xảy ra khi bạn chạy make Debug32, nó sẽ xem nội dung của OBJS là điều kiện tiên quyết, dẫn đến quy tắc đầu tiên ở trên. $(OBJDIR) đã được thay thế bằng giá trị toàn cầu, và điều này khớp với tên mục tiêu trong quy tắc thứ hai cũng được thay thế theo cùng một cách.

Tuy nhiên, khi chúng tôi nhận được vào công thức:

echo mkdir $(OBJDIR) - [email protected] 

$(OBJDIR) chưa được thay thế, vì vậy nó được giá trị biến mục tiêu cụ thể.

Một phiên bản làm việc

.SECONDEXPANSION: 
all: Debug32 

# Object directory set by target 
Debug32: OBJDIR = objdir32 
OBJDIR = wrongdirectory 

Debug32: OBJS = $(addprefix $(OBJDIR)/,obj.o) 
OBJS = wrongobjs 

Debug32: $$(OBJS) 
    echo OBJS are $(OBJS) 
    echo OBJDIR is $(OBJDIR) 

%/obj.o: | % 
    touch [email protected] 

OBJDIRS = objdir32 wrongdirectory anotherdirectory 
$(OBJDIRS): 
# mkdir $(OBJDIR) 
    mkdir [email protected] 

Sự thay đổi chính đang sử dụng $$ trong dòng này:

Debug32: $$(OBJS) 

Với duy nhất một $, tôi nhận được thông báo lỗi

make: *** No rule to make target `wrongobjs', needed by `Debug32'. Stop. 

Tuy nhiên, với số $$, tôi nhận được

echo OBJS are objdir32/obj.o 
OBJS are objdir32/obj.o 
echo OBJDIR is objdir32 
OBJDIR is objdir32 

Việc sử dụng mở rộng phụ đã cho phép truy cập biến mục tiêu cụ thể trong điều kiện tiên quyết.

Thay đổi khác là tôi đã tạo một biến số mục tiêu cụ thể (vì đó là biến số). Để có một quy tắc để xây dựng OBJS bất kỳ giá trị của nó, tôi đã phải sử dụng một quy tắc mẫu:

%/obj.o: | % 

Để tránh việc có một dòng riêng biệt cho từng đối tượng tập tin, bạn có thể làm như sau thay vì:

OBJ_BASENAMES=obj.o obj2.o obj3.o 

$(addprefix %/,$(OBJ_BASENAMES)): | % 
    touch [email protected] # Replace with the proper recipe 

dòng chứa addprefix nở vĩ mô để

%/obj.o %/obj2.o %/obj3.o: | % 

Sau đó chạy make anotherdirectory/obj2.o tạo một thư mục gọi là "anotherdirectory" linh sam st, và tạo ra một tập tin gọi là "obj2.o" bên trong nó.

Lưu ý rằng tất cả các thư mục có thể phải được liệt kê trong OBJDIRS. Không có cách nào để thu thập tất cả các giá trị quy tắc cụ thể của OBJDIR, vì vậy việc liệt kê chúng là lựa chọn tốt nhất. Phương án thay thế là quy tắc % : để xây dựng bất kỳ thư mục nào có khả năng khớp và xây dựng bất kỳ mục tiêu nào, điều này có thể gây rủi ro. (Nếu bạn từ bỏ việc sử dụng các biến mục tiêu cụ thể, có một cách khác để nhận danh sách thư mục có thể được tạo: sử dụng các biến có tên có thể dự đoán như Debug32_OBJDIR và tạo danh sách giá trị của chúng bằng cách sử dụng chức năng.)

Ngoài ra, một nguyên tắc chung mà không cần liệt kê các file đối tượng:

SOURCE=$(basename $(notdir [email protected])).cpp 
DIR=$(patsubst %/,%,$(dir [email protected])) 

%.o: $$(SOURCE) | $$(DIR) 
    touch [email protected] # Replace with proper recipe 

không có tính năng đọc một quy tắc trong bối cảnh của mỗi mục tiêu, thay thế trong các biến mục tiêu cụ thể và có được một quy tắc mới cho mỗi mục tiêu. Quy tắc chung không thể được viết theo cách này bằng cách sử dụng các biến nhắm mục tiêu cụ thể.

+0

này hoạt động, nhưng có vẻ như tôi phải chỉ định Thư mục đối tượng hai lần và mỗi tệp .o hai lần, bao gồm quy tắc mẫu cho mỗi tệp nguồn.Tôi đang đi về điều này tất cả sai? Có vẻ như quá khó để tạo một makefile chung đơn giản cho hai hoặc nhiều mục tiêu. – Jeff

+0

Tôi đã thêm một cách để bao gồm tất cả các tệp đối tượng. Tôi hy vọng nó có thể mà không liệt kê tất cả (một cái gì đó như '% .o: $$ (basename%). Cpp | $$ (dir $$ @)') nhưng tôi đã không có nó để làm việc được nêu ra. –

+0

Tôi đã thêm một phiên bản và ghi chú dễ sử dụng hơn để liệt kê các thư mục. Phương pháp xây dựng thư mục của Mark Galeck có vẻ như nó có thể tốt hơn. –

3

một cách tốt để xử lý

%/obj.o: | % 
    touch [email protected] 

OBJDIRS = objdir32 wrongdirectory anotherdirectory 
$(OBJDIRS): 
    mkdir [email protected] 

là:

%/.: 
    mkdir -p [email protected] 

.SECONDEXPANSION: 

sau đó bạn chỉ có thể viết, đối với bất kỳ mục tiêu đó có thể cần một thư mục

target: prerequisites | $$(@D)/. 
    normal recipe for target 
Các vấn đề liên quan