So I run make lex
and it generates me the lex.yy.c
file, all good
then I run make scanner
, which takes a sourcefile called scanner.c
into compilation, it should simply run cc lex.yy.c scanner.c -o scanner
, but instead it does this:
lex -t scanner.l > scanner.c
cc lex.yy.c scanner.c -o scanner
Why does it decide to run lex -t scanner.l
and output it to scanner.c
effectively overwritting my code? No damned idea, and it's driving me crazy.
my 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
What's going wrong?
Why does it decide to run lex -t scanner.l and output it to scanner.c effectively overwritting my code?
Whenever this happens you have in your build directory a scanner.c
and
a scanner.l
that is more recent than than the scanner.c
When you run make scanner
, the recipe:
scanner: scanner.h scanner.c lex.yy.c
cc lex.yy.c scanner.c -o scanner
requires that its prerequisite scanner.c
shall be made uptodate. You
have provided no recipe that does that, so make falls back
on its database of builtin recipes.
Examine those builtin recipes by running make -p
and you'll find:
%.c: %.l
# recipe to execute (built-in):
@$(RM) $@
$(LEX.l) $< > $@
This builtin recipe will make a file.c
from the matching file.l
by running:
rm file.c # Not echoed
lex -t file.l > file.c
Make finds that the pattern rule for this recipe - %.c: %.l
- is satisfied
by scanner.c
, because scanner.l
exists and more recent that than scanner.c
.
So it uses this recipe to make scanner.c
uptodate:
lex -t scanner.l > scanner.c
thereby clobbering your scanner.c
.
If you do not want make ever to apply this builtin recipe, you can explicitly cancel it by writing just the rule:
%.c: %.l
in your makefile without any recipe.
You can also disable all builtin recipes by passing --no-builtin-rules
on the
make commandline.
However, whenever your expectations about the behaviour of a makefile are sabotaged by the builtin recipes it is strong indication that your expectations are not well-informed as to the usual way of making output files from input files with the tools that your makefile invokes. Make's Catalogue of Built-In Rules:
%.<target-type>: %.<prereq-type>
<command>
...
embody the canonical way of making a <target-type>
file from a <prereq-type>
file with make, so that you don't have to keep writing this recipe yourself in
your makefiles. For example C and C++ programmers who are competent with GNU make
do not write recipes to make .o
files from .c
files or .cpp
files, except in
corner cases, because they are know that the way make will do it automatically
is normally the way they want.
Make's builtin recipe for the %.c: %.l
rule expresses the canonical way of
making file.c
given a file.l
. So ask yourself: If you don't want scanner.c
to be made like that from scanner.l
, and if instead you want lex.yy.c
to
be made from scanner.l
, is it necessary or useful for the file you have called
scanner.l
to be called that, when you also have a quite independent source file
called scanner.c
?
Suppose you took advantage of make's builtin recipes as in this toy example:
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
Then your makefile could be just:
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
which runs like:
$ 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
Note that make runs all these commands:
cc -c -o scanner.o scanner.c
lex -t lexer.l > lexer.c
cc -c -o lexer.o lexer.c
to make lexer.c
, lexer.o
and scanner.o
without you having
written any recipes that tell it how to do so. It also automatically
notices that lexer.c
is an intermediate file - a generated file that
only needs to exist to get from lexer.l
to lexer.o
- so it deletes
it at the end:
rm lexer.c
without being told to.
And this scanner runs like:
$ ./scanner
hello
Found a string: [hello]
42
Found an integer: [42]
42.42
Found a floating-point number: [42.42]
^C
The make
you are using most certainly defines default suffixes, defining implicit rules which conflicts with the ones you define.
See for instance this howto, which explains suffix rules.
At the beginning of your makefiles, you can add a line:
.SUFFIXES:
to tell make
you don't want default suffix rules.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With