help-gnu-utils
[Top][All Lists]
Advanced

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

conditionally update a target


From: Kannan Goundan
Subject: conditionally update a target
Date: 7 Jun 2005 16:38:23 -0700
User-agent: G2/0.2

I have a program called "separator" that reads in a single file
"X.both" and produces "X.h" and "X.cpp".  (This program isn't really
implemented yet.  I'm using a dummy script to test my makefile out.)

Since changing a ".h" file can cause a lot of recompilation, I try to
avoid this when possible.  If the interface part of the "X.both" file
doesn't change, I don't overwrite the output ".h" file.  I do this by
running "diff" and only writing the new ".h" file if it differs from
the old one.

My makefile doesn't work in the following case:
  1. Modify the non-interface part of a "Helper.both" file.
  2. Run "make".  Everything goes as expected.
  3. Run "make" again.  It needlessly runs "separator".
  4. goto 3.

According to the timestamp, "Helper.h" *is* out of date and so Make
tries to rebuild it.  Since GNU Make doesn't maintain it's own
persistent state file, I guess it has no way of knowing that I didn't
modify it the first time.

Does anybody know how to get GNU Make to do what I want?  Is there a
way to use some kind of extra timestamp file?  If possible, I'd like to
avoid having to put the smarts in a shell script.

I've included the relevant files below.  A tarball of everything is
available at "http://cakoose.com/MaybeUpdate.tar.bz2";.  Any guidance
would be appreciated.

- Kannan


----- Makefile:

# The output directory
b = Build
g = Generated
_dummy := $(shell mkdir -p -- $(b) $(g))

# Executable rule

$(b)/Executable: $(b)/Main.o $(b)/Helper.o $(b)/Regular.o
        # Linking
        @cat -- $^ > $@

# Generic rules

.PRECIOUS: $(b)/%.o $(g)/%.cpp $(g)/%.h

$(b)/%.o: %.cpp %.h
        # Compiling $<
        @cat -- $^ > $@

$(b)/%.o: $(g)/%.cpp $(g)/%.h
        # Compiling $<
        @cat -- $^ > $@

$(g)/%.h $(g)/%.cpp: %.both
        # Separating $<
        @./separator $< $(g)/$*.temp $(g)/$*.cpp
        @if diff -- $(g)/$*.temp $(g)/$*.h >/dev/null 2>&1 ; then \
           rm -- $(g)/$*.temp ; \
        else \
           mv -- $(g)/$*.temp $(g)/$*.h ; \
        fi

clean:
        -rm -- $(b)/* $(g)/*

# Dependencies

$(b)/Main.o: $(g)/Helper.h Regular.h


----- separator:

#! /bin/bash

# Separates iface from impl.
#
# Any line that begins with a '#' is an interface line
# and will be copied to the interface file.  All other
# lines will be put in the implementation file.
#
# Usage: COMMAND in-file iface-file impl-file

function die() {
   for line in "$@"; do echo "$line" > /dev/stderr; done
   exit 2
}

[ $# -eq 3 ] || die "Usage: $0 in-file iface-file impl-file"

in_file="$1"
iface_file="$2"
impl_file="$3"

[ -r "$in_file" ] || die "$0: cannot read from file '$in_file'"

sed -n -e '/^ *#/p' -- "$in_file" > "$iface_file" || die "$0: sed 1"
sed    -e '/^ *#/d' -- "$in_file" > "$impl_file"  || die "$0: sed 2"


----- Main.both

# This is an interface line in Main.both
This is an implementation line in Main.both


----- Helper.both

# This is an interface line in Helper.both
This is an implementation line in Helper.both


-----

"Regular.h" are "Regular.cpp" just empty files.



reply via email to

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