16

Using Make is there a nice way to depend on a directories contents.

Essentially I have some generated code which the application code depends on. The generated code only needs to change if the contents of a directory changes, not necessarily if the files within change their content. So if a file is removed or added or renamed I need the rule to run.

My first thought is generate a text file listing of the directory and diff that with the last listing. A change means rerun the build. I think I will have to pass off the generate and diff part to a bash script.

I am hoping somehow in their infinite intelligence might have an easier solution.

Halsafar
  • 2,540
  • 4
  • 29
  • 52
  • Possible duplicate of [Makefile rule that depends on all files under a directory (including within subdirectories)](http://stackoverflow.com/questions/14289513/makefile-rule-that-depends-on-all-files-under-a-directory-including-within-subd) – Løiten May 04 '17 at 11:44

3 Answers3

23

Kudos to gjulianm who got me on the right track. His solution works perfect for a single directory.

To get it working recursively I did the following.

ASSET_DIRS = $(shell find ../../assets/ -type d)
ASSET_FILES = $(shell find ../../assets/ -type f -name '*')

codegen: ../../assets/ $(ASSET_DIRS) $(ASSET_FILES)
     generate-my-code

It appears now any changes to the directory or files (add, delete, rename, modify) will cause this rule to run. There is likely some issue with file names here (spaces might cause issues).

Halsafar
  • 2,540
  • 4
  • 29
  • 52
  • Does this work for deletions? I have not had a chance to test it, but I figured a deletion would just eliminate that file as a dependency. Edit: of course, the directory would be updated in the case of a deletion. I see. – Zoey Hewll Apr 26 '18 at 08:31
  • Maybe the "-name '*'" is not needed, find will already find all files? – Andrew Mackenzie Oct 26 '19 at 22:42
  • Note that "*" keeps hidden files from being included, and is probably desirable – Cireo Apr 16 '21 at 20:21
7

Let's say your directory is called dir, then this makefile will do what you want:

FILES = $(wildcard dir/*)

codegen: dir # Add $(FILES) here if you want the rule to run on file changes too.
    generate-my-code

As the comment says, you can also add the FILES variable if you want the code to depend on file contents too.

gjulianm
  • 834
  • 7
  • 22
1

A disadvantage of having the rule depend on a directory is that any change to that directory will cause the rule to be out-of-date — including creating generated files in that directory. So unless you segregate source and target files into different directories, the rule will trigger on every make.

Here is an alternative approach that allows you to specify a subset of files for which additions, deletions, and changes are relevant. Suppose for example that only *.foo files are relevant.

# replace indentation with tabs if copy-pasting

.PHONY: codegen
codegen:
        find . -name '*.foo' |sort >.filelist.new
        diff .filelist.current .filelist.new || cp -f .filelist.new .filelist.current
        rm -f .filelist.new
        $(MAKE) generate

generate: .filelist.current $(shell cat .filelist.current)
        generate-my-code

.PHONY: clean
clean:
        rm -f .filelist.*

The second line in the codegen rule ensures that .filelist.current is only modified when the list of relevant files changes, avoiding false-positive triggering of the generate rule.

Daira Hopwood
  • 2,264
  • 22
  • 14