I'm making a compiler with Lex and Yacc. To compile my main
, I need yacc to be ran first so the file y.tab.h
exists. However, I do not want that file to be included in the $^
variable when compiling said main
, because the compiler doesn't like getting .h
files as input.
Is there a way to make that rule/recipe depend on y.tab.h
, but exclude it from $^
?
If not, (meaning, if this question is an XY problem), is there a better way to structure this Makefile to achieve that goal ?
Here is my makefile :
targets := ccpy
################################### Options ####################################
cc := gcc
cFlags := -c -O3
cFlagsLenient := $(cFlags) -w
cFlagsHard := $(cFlags) -Wall -pedantic
cFlagsDebug := -c -W -Wall -pedantic -g -Og
ldFlags := -lm -ly -ll
ldFlagsDebug := -g $(ldFlags)
################################### Folders ####################################
binDir := ./bin
debugDir := ./debug
objDir := ./obj
srcDir := ./src
testDir := ./test
################################ File categories ###############################
mains := $(targets:%=$(objDir)/%.o)
srcL := $(wildcard $(srcDir)/*.l)
srcY := $(wildcard $(srcDir)/*.y)
outL := $(srcL:$(srcDir)/%.l=$(srcDir)/%.c)
outY := $(srcY:$(srcDir)/%.y=$(srcDir)/%.c)
objL := $(outL:$(srcDir)/%.c=$(objDir)/%.o)
objY := $(outY:$(srcDir)/%.c=$(objDir)/%.o)
cleanY := $(outY) $(srcY:$(srcDir)/%.y=$(srcDir)/%.h) $(srcDir)/y.output $(srcdir)/y.gv
srcC := $(filter-out $(outL) $(outY), $(wildcard $(srcDir)/*.c))
objC := $(srcC:$(srcDir)/%.c=$(objDir)/%.o)
obj := $(objC) $(objL) $(objY)
objDebug := $(obj:$(objDir)/%.o=$(debugDir)/%.do)
srcTest := $(wildcard $(testDir)/*.c)
tests := $(srcTest:$(testDir)/%.c=$(binDir)/%)
#################################### Recipes ###################################
# Basic
all : $(targets)
again : clean all
debug: $(targets:%=%.db)
test : $(tests)
objects: $(obj)
# Check Variables
info:
$(info mains : $(mains))
$(info srcC : $(srcC))
$(info objC : $(objC))
$(info srcL : $(srcL))
$(info outL : $(outL))
$(info objL : $(objL))
$(info srcY : $(srcY))
$(info outY : $(outY))
$(info objY : $(objY))
$(info obj : $(obj))
$(info objDebug : $(objDebug))
$(info srcTest : $(srcTest))
$(info tests : $(tests))
# Final Executables
$(targets): % : $(objDir)/%.o $(obj)
$(cc) -o $@ $^ $(ldFlags)
# Objects directly compiled from C
$(objC): $(objDir)/%.o : $(srcDir)/%.c
$(cc) $(cFlagsHard) $^ -o $@
# Objects made by Lex and Yacc
$(objL) $(objY): $(objDir)/%.o : $(srcDir)/%.c
$(cc) $(cFlagsLenient) $^ -o $@
# Files made by Lex
$(outL): $(srcDir)/%.c : $(srcDir)/%.l $(outY)
lex -o $@ $<
# Files made by Yacc
$(outY): $(srcDir)/%.c : $(srcDir)/%.y
yacc -v -d -Wcounterexamples --file-prefix="$(srcDir)/y" $^
# Debug executables (outdated)
$(targets:%=%.db): $(objDebug)
$(cc) -o $@ $^ $(ldFlagsDebug)
# Debug objects (outdated)
$(objDebug): $(debugDir)/%.do : $(srcDir)/%.c
$(cc) $(cFlagsDebug) $^ -o $@
# Automated testing
$(tests): $(binDir)/%_test : $(testDir)/%_test.c $(filter-out $(mains), $(obj))
$(cc) -o $@ $^ $(ldFlags)
$@
clean:
rm -rf $(objDir)/*.o $(debugDir)/*.do $(targets) $(tests) $(cleanY) $(outL)
In general, when compiling source files (as opposed to linking), you want to specify a single source file that compiles to a single object file. This is precisely what $<
is for so you just want a rule like:
$(objDir)/%.o: $(srcDir)/%.c
$(CC) $(CFLAGS) -c $< -o $@
whihc will give you a separate compile command for each source. If you try to compile multiple source files with a single invokation of the compiler, you run into trouble because you can't then use -o
to specify the output, so you end up with all the .o files in the same directory as the .c files or in the current working directory (depending on how your C compiler works).