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: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
-oto 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).