help-make
[Top][All Lists]
Advanced

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

Re: Remaking when some "parameter" changes value


From: John Graham-Cumming
Subject: Re: Remaking when some "parameter" changes value
Date: Fri, 17 Feb 2006 11:28:59 +0100
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040208 Thunderbird/0.5 Mnenhy/0.6.0.104

Nick Patavalis wrote:
My problem, in a nutshell, is this: How can I force a project be
remade, when one of the parameters passed to Make changes?  When, for
example, the flags to the compiler, or to the linker, change?

As you are aware there's no built in way to do this in GNU Make. But it is possible to build your own version of what Sun Make calls "keep state" using GNU Make functions.

Here's an example Makefile that I've modified to work as you wish:

    include signature

    all: foo.o bar.o

    FOO=$(FOOEY)

    foo.o: foo.c
        $(call do,$$(COMPILE.C) -DFOO=$$(FOO)$$(@F) -o $$@ $$<)

    bar.o: bar.c
        $(call do,$$(COMPILE.C) -DBAR=$$(BAR) -o $$@ $$<)

    -include foo.sig bar.sig

There are three modifications from a standard Makefile: firstly there's 'include signature' at the start. (You'll see the definition of signature below), then the commands for each rule have been wrapped in $(call do,...) and any $'s in the commands have been quoted with an extra $. Lastly the Makefile includes a .sig file for each .o being created (if the .sig exists, hence the -include instead of include).

The .sig file is generated by code in signature when a rule is run and is used to perform the 'command has changed' checking that you need. Here, for example, is the contents of bar.sig after make has been run for the first time:

    $(eval @ := bar.o)
    $(eval % := )
    $(eval < := bar.c)
    $(eval ? := bar.c)
    $(eval ^ := bar.c)
    $(eval + := bar.c)
    $(eval * := bar)

    bar.o: bar.force

    $(if $(call sne,$(COMPILE.C) -DBAR=$(BAR) -o $@ $<,g++    -c -DBAR=
    -o bar.o bar.c),$(shell touch bar.force))

The first set of lines captures the state of the automatic variables within the rule to make bar.o, the next line says that bar.o depends on a special file called bar.force and lastly there's a rather complex $(if ...) that uses the GMSL (see my sig for the GNU Make Standard Library) string-not-equal (sne) function to check the current expansion of the commands to make bar.o against the previous expansion. It's this $(if ...) that can detect a change in the commands to run a rule. If such a change is detected bar.force is touched and hence bar.o will be rebuilt because bar.force is newer.

The signature include is where the work is done:

    include gmsl

    last_target :=

    dump_var = \$$(eval $1 := $($1))

    define new_rule
    @echo "$(call map,dump_var,@ % < ? ^ + *)" > $S
    @$(if $(wildcard $F),,touch $F)
    @echo $@: $F >> $S
    endef

    define do
    $(eval S := $*.sig)$(eval F := $*.force)$(eval C := $1)
    $(if $(call sne,$@,$(last_target)),$(call new_rule),$(eval
    last_target := $@))
    @echo "$(subst $$,\$$,$$(if $$(call sne,$1,$C),$$(shell touch $F)))"
    >> $S
    $C
    endef

I won't go into all the details of how signature works, but essentially the do macro is responsible for updating the .sig files as needed. I'll write this up for my column on CM Crossroads next month, but you can play around with the code (you need the GMSL and GNU Make 3.80 for this to work) and you'll see that changing a parameter does work.

Here's an example of starting from scratch and then changing the values of FOO and BAR in the Makefile above:

    $ make
    g++    -c -DFOO=foo.o -o foo.o foo.c
    g++    -c -DBAR= -o bar.o bar.c
    $ make
    make: Nothing to be done for `all'.
    $ make BAR=bar
    g++    -c -DBAR=bar -o bar.o bar.c
    $ make BAR=bar
    make: Nothing to be done for `all'.
    $ make BAR=baz
    g++    -c -DBAR=baz -o bar.o bar.c
    $ make BAR=baz FOO=foo
    g++    -c -DFOO=foofoo.o -o foo.o foo.c
    $ make BAR=bar FOO=foo
    g++    -c -DBAR=bar -o bar.o bar.c
    $ make
    g++    -c -DFOO=foo.o -o foo.o foo.c
    g++    -c -DBAR= -o bar.o bar.c

The only limitation of this scheme is that if you change the commands in a rule by editing the Makefile you need to do a clean build or at least delete the corresponding .sig file so that it gets remade.

John.
--
John Graham-Cumming
address@hidden

Home: http://www.jgc.org/
Blog: http://www.jgc.org/blog/

POPFile: http://getpopfile.org/
GNU Make Standard Library: http://gmsl.sf.net/
GNU Make Debugger: http://gmd.sf.net/
Fast, Parallel Builds: http://www.electric-cloud.com/

Sign up for my Spam and Anti-spam Newsletter
at http://www.jgc.org/




reply via email to

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