cmakefileyacclex

Makefile : Exclude a dependency from $^


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)


Solution

  • 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).