tags:

views:

109

answers:

3

I'm using a makefile to automate some document generation. I have several documents in a directory, and one of my makefile rules will generate an index page of those files. The list of files itself is loaded on the fly using list := $(shell ls documents/*.txt) so I don't have to bother manually editing the makefile every time I add, delete, or rename a document. Naturally, I want the index-generation rule to trigger when number/title of files in the documents directory changes, but I don't know how to set up the prerequisites to work in this way.

I could use .PHONY or something similar to force the index-generation to run all the time, but I'd rather not waste the cycles. I tried piping ls to a file list.txt and using that as a prerequisite for my index-generation rule, but that would require either editing list.txt manually (trying to avoid it), or auto-generating it in the makefile (this changes the creation time, so I can't use list.txt in the prerequisite because it would trigger the rule every time).

+2  A: 

You can put a dependency on the directory itself for the list file, e.g.

list.txt: documents
        ls documents/*.txt 2>/dev/null > $@ || true

Every time you add or remove a file in the documents directory, the directory's timestamp will be altered and make will do the right thing.

hlovdal
Does changing a file name also change the timestamp?
Yes, by changing the name of a file you are modifying the directory and the timestamp of the directory will be updated (the timestamp of the file itself will remain unchanged).
hlovdal
Perhaps this is filesystem specific, but editing a file inside the directory will also increase the timestamp. The rule described above would run even if no files were added, deleted, or renamed. It wouldn't run everytime, but editing the contents of a file shouldn't trigger a regeneration of the list.
Yes, the rule might be triggered too often (false positives) but it will never miss any required updates (i.e. no false negatives), and I find that most important. http://en.wikipedia.org/wiki/Type_I_and_type_II_errors.
hlovdal
+2  A: 

If you need a dependency on the number of files, then... why not just depend on the number itself? The number will be represented as a dummy file that is created when the specified nubmer of files is in the documents directory.

NUMBER=$(shell ls documents/*.txt | wc -l).files
# This yields name like 2.files, 3.files, etc...
# .PHONY $(NUMBER) -- NOT a phony target!

$(NUMBER):
        rm *.files    # Remove previous trigger
        touch $(NUMBER)

index.txt: $(NUMBER)
        ...generate index.txt...

While number of files is one property to track, instead you may depend on a hash of a directory listing. It's very unlikely that hash function will be the same for two listings that occur in your workflow. Here's an example:

NUMBER=$(shell ls -l documents/*.txt | md5sum | sed 's/[[:space:]].*//').files

Note using -l -- this way you'll depend on full listing of files, which includes modification time, sizes and file names. Bu if you don't need it, you may drop the option.

Note: sed was needed because on my system md5sum yields some stuff after the hash-sum itself.

Pavel Shved
+1 oh, that's clever.
Beta
I wonder if this solution can be adjusted to account for file renaming and not just number of files.
@goathens, it can, I'm fixing the answer. But, actually, while your intent was clear, you asked for dependency on a *number* of files ;-)
Pavel Shved
question edited for clarity.
A: 

Here's a solution that updates the index if and only if the set of files has changed:

list.txt.tmp: documents
    ls $</*.txt > $@

list.txt: list.txt.tmp
    cmp -s $< $@ || cp $< $@

index.txt: list.txt
    ...generate index.txt...

Thanks to the "cmp || cp", the ctime of "list.txt" does not change unless the output of the "ls" has changed.

slowdog