所以我运行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
为什么决定运行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
出了什么问题?
答案 0 :(得分:8)
为什么决定运行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
要求其先决条件scanner.c
必须更新。您
没有提供这样做的配方,所以要回落
在其内置食谱数据库中。
运行make -p
检查这些内置食谱,您会发现:
%.c: %.l
# recipe to execute (built-in):
@$(RM) $@
$(LEX.l) $< > $@
此内置配方将通过运行匹配file.c
来生成file.l
:
rm file.c # Not echoed
lex -t file.l > file.c
让我发现满足此配方的模式规则 - %.c: %.l
-
截至scanner.c
,因为scanner.l
存在且比scanner.c
更近。
因此,它使用此配方使scanner.c
更新:
lex -t scanner.l > scanner.c
从而破坏了您的scanner.c
。
如果你不想让make应用这个内置配方,你可以明确地 通过写下规则取消它:
%.c: %.l
没有任何配方的makefile中的。
您还可以通过传递--no-builtin-rules
来禁用所有内置食谱
制作命令行。
然而,无论何时你对makefile的行为都有所期待 被内置食谱破坏它强烈表明你的期望 对于从输入文件制作输出文件的常用方法,他们并不了解情况 使用makefile调用的工具。 制作Catalogue of Built-In Rules:
%.<target-type>: %.<prereq-type>
<command>
...
体现了从<target-type>
制作<prereq-type>
文件的规范方式
带有make的文件,这样你就不必自己写这个食谱了
你的文件。例如,能够胜任GNU make的C和C ++程序员
不要编写配方来制作.o
个文件或.c
文件中的.cpp
个文件,除了
极端情况,因为他们知道make的方式会自动完成
通常是他们想要的方式。
制作%.c: %.l
规则的内置配方表达了规范的方式
将file.c
设为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); }
%%
<强> scanner.c 强>
#include "scanner.h"
int main(void) {
yylex();
return 0;
}
<强> scanner.h 强>
#ifndef SCANNER_H
#define SCANNER_H
extern int yylex(void);
#endif
然后你的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
的运行方式如下:
$ 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
请注意,make运行所有这些命令:
cc -c -o scanner.o scanner.c
lex -t lexer.l > lexer.c
cc -c -o lexer.o lexer.c
在没有你的情况下制作lexer.c
,lexer.o
和scanner.o
写下任何告诉它如何做的食谱。它也是自动的
通知lexer.c
是中间文件 - 生成的文件
只需要存在才能从lexer.l
转到lexer.o
- 所以它会删除
最后:
rm lexer.c
没有被告知。
此扫描仪的运行方式如下:
$ ./scanner
hello
Found a string: [hello]
42
Found an integer: [42]
42.42
Found a floating-point number: [42.42]
^C
答案 1 :(得分:5)
您使用的make
肯定会定义默认后缀,定义与您定义的规则冲突的隐式规则。
例如,请参阅此howto,其中说明了后缀规则。
在makefile的开头,您可以添加一行:
.SUFFIXES:
告诉make
您不想要默认后缀规则。
答案 2 :(得分:1)
好吧,你创建了以下依赖项,它没有创建命名目标,因此总是调用来尝试创建它
lex: scanner.l <-- incorrect target name, not created from source.
lex scanner.l > lex.yy.c
此依赖项尝试创建名为lex
的文件,但不会从下面的命令行创建。也许你想要这个,而不是
lex.yy.c: scanner.l
lex scanner.l -olex.yy.c
甚至
lex.yy.c: scanner.l
lex scanner.l -o$@
(使用-o
选项,因为lex(1)
并不会在标准输出上生成扫描程序。顺便说一下,我认为,但不确定,lex要求选项参数直接联系选项-o
,请参见手册页
您有一个默认规则,可以从同一基本名称的.c
文件创建.l
文件。规则类似于
.l.c:
$(LEX) $(LFLAGS) -o$@ $<
使用LEX
变量中指示的标志在变量LFLAGS
中执行命令,从同名的lex输入生成源C文件,但扩展名为.l
。