help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: "Advanced Auto-Dependency Generation" and lex/yacc (again)


From: Mike Shal
Subject: Re: "Advanced Auto-Dependency Generation" and lex/yacc (again)
Date: Fri, 27 Jan 2006 07:37:47 -0500

On 1/26/06, Williams, James P <address@hidden> wrote:
>
>
> I'm trying to implement automatic dependency generation using the excellent
> idea posted here.
>
>    http://make.paulandlesley.org/autodep.html
>
> The following email seems to describe the problem I'm having.
>
>
> http://lists.gnu.org/archive/html/help-make/2001-12/msg00014.html
>
> In my case, however, I'm trying to implement this in a general way that can
> be #included by Makefiles from many different applications.  Also, files
> other than lex.c files can #include yacc.h.  So the dependency graph could
> look like this.
>
>    prog.exe: goo.o lex.o yacc.o
>
>    goo.o: goo.c yacc.h
>    lex.o: lex.c yacc.h
>    yacc.o: yacc.c
>
>    lex.c: lex.l
>    yacc.c yacc.h: yacc.y
>
> And just because there's a lex.l doesn't mean there's an associated yacc.y.
>
> How can I teach make these dependencies in a general way without having to
> write them by hand, using "Advanced Auto-Dependencies"?  I don't see a way
> to detect the goo.o/yacc.h dependency because it requires that yacc be run
> first.  I'm coming to the conclusion that I must return to automatically
> building the dependency files using implicit rules, i.e., "Basic
> Auto-Dependencies" (see first link above), with all of the problems that
> introduces.  Is that really the best way to do this?
>
> Thanks,
>
> Jim

It may be slightly inefficient, but you can do it automatically using
a kind of two-pass solution. On the first pass, all the files that
generate other sources are run (such as the lex & yacc commands). On
the second pass, since your generated .h and .c files exist, you can
wildcard the sources to build the program. Hopefully this commented
example will help illustrate what I mean... :)

#### Begin Makefile ####
$(warning make is starting)

# Get a list of all the .c srcs, and find their corresponding objects and
# automatically generated dependency files
srcs := $(wildcard *.c)
objs := $(srcs:.c=.o)
deps := $(srcs:.c=.d)

# The program depends on all the objects in the current directory. Note the
# first time through, $(objs) is incomplete (it doesn't include the yacc and lex
# generated sources). We don't care though, since this rule won't take affect
# until those sources are created and make reloads.
prog: $(objs)
        gcc $^ -o $@

# Get the dependencies from all of the compiler-generated files. Note that
# nowhere in this file is a rule with $(deps) as a target. This prevents
# make from reloading unnecessarily.
-include $(deps)

# Generic rule to create an object from a .c file. The -MMD creates the .d
# file automatically.
%.o: %.c
        gcc -MMD -c $< -o $@

# Find all the lex srcs and their corresponding autodependency stub files (.ad).
# These files are used as a target so that make knows to reload after they
# are changed. This way the second time make loads, the lex.c file will be
# present for the c srcs wildcard. Note that the .ad files don't actually
# contain any dependency information.
lsrcs := $(wildcard *.l)
ldeps := $(lsrcs:.l=.ad)
$(ldeps): %.ad: %.l
        @echo "lex $<"; touch $@; touch $(<:.l=.c)

# Similar to above, only for yacc srcs. They could probably be combined in
# some fashion if you want a smaller Makefile :).
ysrcs := $(wildcard *.y)
ydeps := $(ysrcs:.y=.ad)
$(ydeps): %.ad: %.y
        @echo "yacc $<"; touch $@; touch $(<:.y=.c); touch $(<:.y=.h)

# Include the empty autodependency stubs. The first time make is run in a
# clean area, these .ad files won't exist. So make will build them using
# the lex & yacc rules, then reload. This means no .c -> .o compilation
# happens on the first pass. Also, we don't want make to be dependent on
# these files in a "clean" mode, otherwise a "make clean" will try to
# rebuild the lex and yacc sources before removing them.
ifeq (,$(filter clean,$(MAKECMDGOALS)))
  -include $(ydeps) $(ldeps)
endif

# Clean out the objects, dependencies, autodependency stubs, and other
# generated files. You could use patsubst or similar functions to generate
# the lex.c, yacc.c, and yacc.h to be automatically cleaned. Alternatively,
# you could set up the lex and yacc rules to place their generated output
# in another directory. The clean could then just 'rm -rf' that directory.
clean: ; rm -f $(objs) $(deps) $(ldeps) $(ydeps) lex.c yacc.c yacc.h prog

#### End Makefile ####

I think this results in what you would expect (I put the warning at
the top to show when make reloads)...

# A clean build
$ make
Makefile:1: make is starting
lex lex.l
yacc yacc.y
Makefile:1: make is starting
gcc -MMD -c goo.c -o goo.o
gcc -MMD -c lex.c -o lex.o
gcc -MMD -c yacc.c -o yacc.o
gcc goo.o lex.o yacc.o -o prog

# Editing a yacc file -  goo.o is rebuilt since goo.d contains the
# dependency on yacc.h automatically.
$ touch yacc.y
$ make
Makefile:1: make is starting
yacc yacc.y
Makefile:1: make is starting
gcc -MMD -c goo.c -o goo.o
gcc -MMD -c yacc.c -o yacc.o
gcc goo.o lex.o yacc.o -o prog

# Editing a regular .c file - make only loads the makefile once
$ touch goo.c
$ make
Makefile:1: make is starting
gcc -MMD -c goo.c -o goo.o
gcc goo.o lex.o yacc.o -o prog

Anyway, the clean target would still need to be fixed up a bit, and
running in multiple directories will probably require additional
hackery. Hopefully that will help some though :)

-Mike




reply via email to

[Prev in Thread] Current Thread [Next in Thread]