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

Jos*_*ose 6 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 
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)

出了什么问题?

Mik*_*han 8

为什么它决定运行lex -t scanner.l并将其输出到scanner.c有效地覆盖我的代码?

一旦发生这种情况,你必须在生成目录scanner.cscanner.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.llexer.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)


ton*_*nio 5

make您使用的肯定定义了默认后缀,定义与定义的那些冲突潜规则.

请参阅此howto,它解释了后缀规则.

在makefile的开头,您可以添加一行:

.SUFFIXES:
Run Code Online (Sandbox Code Playgroud)

告诉make你不要想要默认的后缀规则.