tags:

views:

54

answers:

3

I'm using makefiles to convert an internal file format to an XML file which is sent to other colleagues. They would make changes to the XML file and send it back to us (Don't ask, this needs to be this way ;)). I'd like to use my makefile to update the internal files when this XML changes.

So I have these rules:

%.internal: $(DATAFILES)
       # Read changes from XML if any
       # Create internal representation here

%.xml: %.internal
       # Convert to XML here

Now the XML could change because of the workflow described above. But since no data files have changed, make would tell me that file.internal is up-to-date. I would like to avoid making %.internal target phony and a circular dependency on %.xml obviously doesn't work.

Any other way I could force make to check for changes in the XML file and re-build %.internal?

A: 

You want to allow two different actions: making the xml file from the internal file, and making the internal file from the xml file. Since Make knows only the modification times, it knows which target is older but not whether it should be remade. So put in another file as a flag to record when either action was last taken, and make that your primary target; if either target is newer than the flag, it has been modified by something other than these actions, and make should rebuild the older target (and then touch the flag).

There are several ways to implement this. In some versions of Make (such as recent versions of GNUMake) you can write double-colon rules, so that Make will rebuild a target differently, based on which preq triggered it:

%.flag:: %.internal
    # convert $*.internal to $*.xml
    touch $@

%.flag:: %.xml
    # rewrite $*.internal based on $*.xml
    touch $@

A less elegant but more portable way is to look at $? and rebuild the other file:

%.flag: %.xml %.internal
ifeq ($?,$*.internal)
    # convert $*.internal to $*.xml
else
    # rewrite $*.internal based on $*.xml
endif
    touch $@
Beta
A: 

I think you could do something like this:

all: .last-converted-xml .last-converted-internal

.last-converted-internal: *.internal
    ./internal-2-xml $?
    touch $@ .last-converted-xml

.last-converted-xml: *.xml
    ./xml-2-internal $?
    touch $@ .last-converted-internal

This runs "xml-convert" on any .xml files newer than an arbitrary marker file, ".last-converted". The $? should give you a list of all dependencies (*.xml) that are newer than the marker file.

Of course, the xml-convert program will have to be written to take a list of xml files and process each one.

I'm not sure from the question whether you actually need the .internal file, or if that was just an attempt to get the makefile working. So, either your "xml-convert" program can convert each .xml file in place, or it can also generate file.internal as well if you need it.

Jay Walker
This will work, but it will do a lot of unnecessary rebuilding: if Make rebuilds the xml file this time, then it will rebuild the internal file next time, and vice versa. This will happen with every internal-xml pair, every time you run Make.
Beta
I was assuming that "xml-convert" generates both the.internal file and the updated .xml file; the makefile has no rules for the .internal file at all, so no, the loop you describe will not happen.
Jay Walker
Why should the last-converted rule modify the xml file? And what happens if someone modifies a data file?
Beta
Okay, I misunderstood; I've updated the example so that it works whether he updates the .xml or whether he updates the .internal file.The only thing is, there's no way to decide what to do if *both* the xml file and the .internal file change; in that case there needs to be some kind of merge. So this makefile assumes that it will be run anytime xml files only have been updated, and anytime internal files only have been updated and not at any time when both xml and internal files have been updated.
Jay Walker
Now if someone modifies an internal file, the first rule updates the xml and touches last-converted-xml, leaving last-converted-internal still out of date. The first rule will run again every time you run make, until someone modifies an xml file, whereupon the second rule will run every time. (Want to know how I can spot these things? I've been bitten by my own makefiles *many* times!)
Beta
Beta- look closely, the touch for either target touches both flag files. If you actually run the makefile you will see that it is correct. As I said, the only deficiency is that it has to be run when only the internal or only the xml files have been updated; it is not entirely correct when a mixture of xml files and internal files both have been updated since the last time it was run. To handle that case, you would need to add a separate flag file for each individual .xml file and each individual .internal file.
Jay Walker
A: 

Use the -W option of make to have make think one of the data files has changed:

make -W somedatafile

This will cause make to think somedatafile has been modified without actually changing it's modification time.


Would it be possible to use different names for the XML file? The file you create from the internal format would have one name and the file your colleagues send you another? If they used different names there would be no circular dependency.

Robert Menteer