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

时间:2017-09-17 07:01:39

标签: c 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

出了什么问题?

3 个答案:

答案 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.cscanner.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.clexer.oscanner.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,请参见手册页

注1

您有一个默认规则,可以从同一基本名称的.c文件创建.l文件。规则类似于

.l.c:
    $(LEX) $(LFLAGS) -o$@ $<

使用LEX变量中指示的标志在变量LFLAGS中执行命令,从同名的lex输入生成源C文件,但扩展名为.l