2017-09-17 26 views
6

Vì vậy, tôi chạy make lex và nó tạo cho tôi những tập tin lex.yy.c, tất cả đều tốtTại sao makefile lại nhấn mạnh vào việc biên dịch những thứ không nên?

sau đó tôi chạy make scanner, trong đó có một sourcefile gọi scanner.c thành lập, nó chỉ đơn giản nên chạy cc lex.yy.c scanner.c -o scanner, nhưng thay vào đó nó thực hiện điều này:

lex -t scanner.l > scanner.c 
cc lex.yy.c scanner.c -o scanner 

Tại sao quyết định chạy lex -t scanner.l và xuất nó thành scanner.c ghi đè mã của tôi một cách hiệu quả? Không có ý tưởng chết tiệt, và nó làm tôi phát điên.

makefile của tôi:

scanner: scanner.h scanner.c lex.yy.c 
    cc lex.yy.c scanner.c -o scanner 

lex: scanner.l 
    lex scanner.l > lex.yy.c 

clean: 
    rm lex.yy.c 
    rm scanner 

gì đang xảy ra vậy?

Trả lời

8

Tại sao quyết định chạy lex -t scanner.l và xuất nó ra scanner.c có hiệu quả ghi đè mã của tôi?

Bất cứ khi nào điều này xảy ra bạn có trong thư mục xây dựng của bạn một scanner.c và một scanner.l rằng gần đây hơn so với scanner.c

Khi bạn chạy make scanner, công thức:

scanner: scanner.h scanner.c lex.yy.c 
    cc lex.yy.c scanner.c -o scanner 

yêu cầu điều kiện tiên quyết của nó scanner.c sẽ được thực hiện uptodate. Bạn đã không cung cấp công thức nào để thực hiện điều đó, vì vậy hãy quay trở lại trên cơ sở dữ liệu về các công thức nấu sẵn.

Kiểm tra những công thức nấu ăn BUILTIN bằng cách chạy make -p và bạn sẽ tìm thấy:

%.c: %.l 
# recipe to execute (built-in): 
    @$(RM) [email protected] 
    $(LEX.l) $< > [email protected] 

thức dựng sẵn này sẽ làm cho một file.c từ phù hợp với file.l bằng cách chạy:

rm file.c # Not echoed 
lex -t file.l > file.c 

Make thấy rằng mô hình quy tắc cho công thức này - %.c: %.l - được đáp ứng bởi scanner.c, bởi vì scanner.l tồn tại và gần đây hơn scanner.c. Vì vậy, nó sử dụng công thức này để làm cho scanner.c uptodate:

lex -t scanner.l > scanner.c 

do đó clobbering scanner.c của bạn.

Nếu bạn không muốn làm bao giờ hết để áp dụng công thức dựng sẵn này, bạn rõ ràng có thể hủy bỏ nó bằng cách viết chỉ là quy tắc:

%.c: %.l 

trong makefile của bạn mà không cần bất kỳ công thức.

Bạn cũng có thể tắt tất cả công thức nấu sẵn bằng cách chuyển --no-builtin-rules trên làm dòng lệnh.

Tuy nhiên, bất cứ khi nào mong đợi của bạn về hành vi của một makefile đang phá hoại bởi các công thức nấu ăn được xây dựng trong nó là bằng chứng mạnh mẽ rằng sự mong đợi của bạn không đầy đủ thông tin như cách thông thường làm cho các file đầu ra từ tập tin đầu vào với các công cụ mà makefile của bạn gọi ra. Make của Catalogue of Built-In Rules:

%.<target-type>: %.<prereq-type> 
    <command> 
    ... 

là hiện thân của cách kinh điển thực hiện một tập tin <target-type> từ một tập tin <prereq-type> với make, do đó bạn không cần phải tiếp tục viết về công thức này mình trong makefiles của bạn. Ví dụ, các lập trình viên C và C++ có thẩm quyền với GNU làm cho không viết các công thức để tạo các tệp .o từ các tệp .c hoặc .cpp, ngoại trừ các trường hợp góc vì họ biết cách tự động làm điều đó tự động thường là theo cách họ muốn.

Công thức dựng sẵn của Make cho quy tắc %.c: %.l thể hiện cách kinh điển của làm file.c cho một file.l. Vì vậy, hãy tự hỏi: Nếu bạn không muốn scanner.c phải được thực hiện như thế từ scanner.l, và nếu thay vào đó bạn muốn lex.yy.c để được làm từ scanner.l, nó là cần thiết hoặc hữu ích cho các tập tin mà bạn đã gọi scanner.l được được gọi là, khi bạn cũng có một tệp nguồn khá độc lập được gọi là scanner.c?

Giả sử bạn đã tận dụng công thức nấu ăn BUILTIN làm như trong ví dụ đồ chơi này:

lexer.l

%{ 
#include <stdio.h> 
%} 
%% 
[ \t] ; 
[0-9]+\.[0-9]+ { printf("Found a floating-point number: [%s]\n",yytext); } 
[0-9]+ { printf("Found an integer: [%s]\n",yytext); } 
[a-zA-Z0-9]+ { printf("Found a string: [%s]\n",yytext); } 
%% 

scanner.c

#include "scanner.h" 

int main(void) { 
    yylex(); 
    return 0; 
} 

máy quét.h

#ifndef SCANNER_H 
#define SCANNER_H 

extern int yylex(void); 

#endif 

Sau đó makefile của bạn có thể chỉ là:

Makefile

SRCS := scanner.c lexer.c 
OBJS := $(SRCS:.c=.o) 
LDLIBS := -lfl 

.PHONY: all clean 

all: scanner 

scanner: $(OBJS) 
    $(LINK.o) -o [email protected] $^ $(LDLIBS) 

scanner.o: scanner.h 

clean: 
    rm -f scanner *.o 

mà chạy như:

$ make 
cc -c -o scanner.o scanner.c 
lex -t lexer.l > lexer.c 
cc -c -o lexer.o lexer.c 
cc -o scanner scanner.o lexer.o -lfl 
rm lexer.c 

Lưu ý rằng làm cho chạy tất cả các lệnh này:

cc -c -o scanner.o scanner.c 
    lex -t lexer.l > lexer.c 
    cc -c -o lexer.o lexer.c 

để làm lexer.c, lexer.oscanner.o mà không cần phải viết bất kỳ công thức nấu ăn mà nói với nó như thế nào để làm như vậy. Nó cũng tự động thông báo rằng lexer.c là một file trung gian - một tập tin được tạo ra mà chỉ cần tồn tại để có được từ lexer.l để lexer.o - vì vậy nó sẽ xoá nó ở cuối:

rm lexer.c 

không được nói đến .

Và máy quét này chạy như:

$ ./scanner 
hello 
Found a string: [hello] 

42 
Found an integer: [42] 

42.42 
Found a floating-point number: [42.42] 

^C 
5

make bạn đang sử dụng chắc chắn nhất định nghĩa hậu tố mặc định, xác định các quy tắc tiềm ẩn xung đột với quy tắc bạn xác định.

Xem ví dụ này howto, giải thích các quy tắc hậu tố.

Vào đầu makefiles, bạn có thể thêm một dòng:

.SUFFIXES: 

nói make bạn không muốn quy tắc hậu tố mặc định.

1

Vâng, bạn đã tạo ra sự phụ thuộc sau đây, mà không tạo ra các mục tiêu được đặt tên, và vì vậy được gọi là luôn luôn, để cố gắng tạo ra nó

lex: scanner.l   <-- incorrect target name, not created from source. 
    lex scanner.l > lex.yy.c 

Phụ thuộc này cố gắng tạo tệp có tên lex, nhưng không có tệp nào được tạo từ dòng lệnh bên dưới. Có lẽ bạn muốn điều này, thay vì

lex.yy.c: scanner.l 
    lex scanner.l -olex.yy.c 

hoặc thậm chí

lex.yy.c: scanner.l 
    lex scanner.l [email protected] 

(sử dụng -o tùy chọn, như lex(1) không sản xuất máy quét trên đầu ra tiêu chuẩn. Bằng cách này tôi nghĩ, nhưng không chắc chắn, lex đòi hỏi sự lập luận tùy chọn để được trực tiếp tiếp xúc với các tùy chọn -o, xem man page)

GHI CHÚ 1

Bạn h ave quy tắc mặc định để tạo tệp .c từ tệp .l có cùng tên cơ sở. Quy tắc là cái gì đó như

.l.c: 
    $(LEX) $(LFLAGS) [email protected] $< 

thực thi lệnh trong biến LEX với cờ đã nêu trong LFLAGS biến, tạo ra các tập tin nguồn C từ một đầu vào lex cùng tên, nhưng với .l mở rộng.

Các vấn đề liên quan