为什么makefile坚持编译不应该编译的东西?

所以我运行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

出了什么问题?

解决方法

Why does it decide to run lex -t scanner.l and output it to scanner.c effectively overwritting my code?

每当发生这种情况时,您在构建目录中都有一个scanner.c
扫描仪.l比scanner.c更新

当您运行make scanner时,配方:

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

要求其先决条件scan.c应为uptodate.您
没有提供这样做的配方,所以要回落
在其内置食谱数据库.

通过运行make -p来检查那些内置的配方,你会发现:

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

这个内置配方将通过运行以下命令从匹配的file.l生成file.c:

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

Make发现此配方的模式规则 – %.c:%.l – 已满足
通过scanner.c,因为scanner.l存在,并且比scanner.c更新.
所以它使用这个配方来使scan.c uptodate:

lex  -t scanner.l > 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程序员
不要编写从.c文件或.cpp文件制作.o文件的配方,除了
极端情况,因为他们知道make的方式会自动完成
通常是他们想要的方式.

Make的内置配方为%.c:%.l规则表达了规范的方式
在给出file.l的情况下制作file.c所以问问自己:如果你不想要scanner.c
要像scan.l那样制作,如果你想要lex.yy.c
可以从scanner.l制作,对你调用的文件是必要的还是有用的
scan.l被调用,当你还有一个非常独立的源文件
叫scan.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可能只是:

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

相关文章

首先GDB是类unix系统下一个优秀的调试工具, 当然作为debug代...
1. C语言定义1个数组的时候, 必须同时指定它的长度.例如:int...
C++的auto关键字在C+⬑新标准出来之前基本...
const关键字是用于定义一个不该被改变的对象,它的作用是告诉...
文章浏览阅读315次。之前用C语言编过链表,这几天突然想用C+...
文章浏览阅读219次。碰到问题就要记录下来,防止遗忘吧。文章...