所以我跑了make lex,它生成了我的lex.yy.c文件,一切都很好
然后我运行make scanner,它需要一个调用scanner.c编译的源文件,它应该只运行cc lex.yy.c scanner.c -o scanner,但它会这样做:
lex -t scanner.l > scanner.c
cc lex.yy.c scanner.c -o scanner
Run Code Online (Sandbox Code Playgroud)
为什么它决定运行lex -t scanner.l并输出它以scanner.c有效地覆盖我的代码?没有该死的想法,这让我发疯了.
我的makefile:
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
Run Code Online (Sandbox Code Playgroud)
出了什么问题?
为什么它决定运行lex -t scanner.l并将其输出到scanner.c有效地覆盖我的代码?
一旦发生这种情况,你必须在生成目录scanner.c和scanner.l比高于近期多scanner.c
当你跑步时make scanner,食谱:
scanner: scanner.h scanner.c lex.yy.c
cc lex.yy.c scanner.c -o scanner
Run Code Online (Sandbox Code Playgroud)
要求其先决条件scanner.c是最新的.你没有提供这样做的配方,所以make回到了它的内置食谱数据库.
通过运行检查那些内置食谱make -p,你会发现:
%.c: %.l
# recipe to execute (built-in):
@$(RM) $@
$(LEX.l) $< > $@
Run Code Online (Sandbox Code Playgroud)
这个内置的配方将通过运行file.c来匹配file.l:
rm file.c # Not echoed
lex -t file.l > file.c
Run Code Online (Sandbox Code Playgroud)
让我们发现这个食谱的模式规则%.c: %.l- 是满足的scanner.c,因为它scanner.l存在并且比最近更新scanner.c.所以它使用这个配方来制作scanner.cuptodate:
lex -t scanner.l > scanner.c
Run Code Online (Sandbox Code Playgroud)
从而破坏你的scanner.c.
如果你不想让make永远地应用这个内置配方,你可以通过只编写规则明确地取消它:
%.c: %.l
Run Code Online (Sandbox Code Playgroud)
在没有任何配方的makefile中.
您还可以通过传递make命令行来禁用所有内置配方--no-builtin-rules.
但是,每当您对内置配方破坏makefile行为的期望时,强烈表明您对使用makefile调用的工具从输入文件生成输出文件的常用方法的期望不明确.制作内置规则目录:
%.<target-type>: %.<prereq-type>
<command>
...
Run Code Online (Sandbox Code Playgroud)
体现了
使用make 从文件制作文件的规范方法,这样您就不必自己在makefile中自己编写这个食谱.例如,能够胜任GNU make的C和C++程序员不会编写配方来制作文件或文件中的文件,除非是在极端情况下,因为他们知道make的方式通常是他们想要的方式.<target-type><prereq-type>.o.c.cpp
Make的%.c: %.l规则内置配方表达了file.c给出a 的规范方法file.l.所以问问你自己:如果你不做希望scanner.c
从制造这样的scanner.l,如果不是你想lex.yy.c从制作scanner.l,是有必要或已调用的文件很有用
scanner.l要调用的,当你也有一个相当独立源文件叫scanner.c?
假设你在这个玩具示例中利用了make的内置配方:
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); }
%%
Run Code Online (Sandbox Code Playgroud)
scanner.c
#include "scanner.h"
int main(void) {
yylex();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
scanner.h
#ifndef SCANNER_H
#define SCANNER_H
extern int yylex(void);
#endif
Run Code Online (Sandbox Code Playgroud)
然后你的makefile可能只是:
Makefile文件
SRCS := scanner.c lexer.c
OBJS := $(SRCS:.c=.o)
LDLIBS := -lfl
.PHONY: all clean
all: scanner
scanner: $(OBJS)
$(LINK.o) -o $@ $^ $(LDLIBS)
scanner.o: scanner.h
clean:
rm -f scanner *.o
Run Code Online (Sandbox Code Playgroud)
运行方式如下:
$ 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
Run Code Online (Sandbox Code Playgroud)
请注意,make运行所有这些命令:
cc -c -o scanner.o scanner.c
lex -t lexer.l > lexer.c
cc -c -o lexer.o lexer.c
Run Code Online (Sandbox Code Playgroud)
制作lexer.c,lexer.o并且scanner.o没有你写过任何告诉它如何这样做的食谱.它还可以自动注意到lexer.c是一个中间文件 -生成的文件,该文件只需要存在从获得lexer.l到lexer.o-所以在年底将其删除:
rm lexer.c
Run Code Online (Sandbox Code Playgroud)
没有被告知.
这个扫描仪运行如下:
$ ./scanner
hello
Found a string: [hello]
42
Found an integer: [42]
42.42
Found a floating-point number: [42.42]
^C
Run Code Online (Sandbox Code Playgroud)
在make您使用的肯定定义了默认后缀,定义与定义的那些冲突潜规则.
请参阅此howto,它解释了后缀规则.
在makefile的开头,您可以添加一行:
.SUFFIXES:
Run Code Online (Sandbox Code Playgroud)
告诉make你不要想要默认的后缀规则.