64

One rule in my Makefile zips an entire directory (res/) into a ZIP file. Obviously, this rule needs to execute when any file under the res/ directory changes. Thus, I want the rule to have as a prerequisite all files underneath that directory. How can I implement this rule?

In Bash with the globstar option enabled, you can obtain a list of all the files in that directory using the wildcard pattern res/**/*. However, it doesn't seem to work if you specify it as a prerequisite in the Makefile:

filename.jar: res/**/*

Even after touching a file in res/, Make still reports

make: `filename.jar' is up to date.

so clearly it is not recognizing the pattern.

If I declare the directory itself as a prerequisite:

filename.jar: res

then Make will not re-execute when a file is modified (I think make only looks at the modified date of the directory itself, which only changes when immediate children are added, removed, or renamed).

Community
  • 1
  • 1
Mechanical snail
  • 29,755
  • 14
  • 88
  • 113

1 Answers1

81

This:

filename.jar: $(wildcard res/**/*)

seems to work, at least on some platforms.

EDIT:

Or better, just cut the knot:

filename.jar: $(shell find res -type f)
Beta
  • 96,650
  • 16
  • 149
  • 150
  • 2
    The `wildcard` suggestion doesn't work for me, but `find` does work. (I used just `$(shell find res)` in order to trigger it when files are deleted as well.) – Mechanical snail Jan 12 '13 at 22:10
  • 8
    Caveat: If you *remove* one of the input files after making the jar, make will not notice. To be able to detect file removals, you could store the list in a file, similar to http://stackoverflow.com/questions/3236145/force-gnu-make-to-rebuild-objects-affected-by-compiler-definition/3237349#3237349 – slowdog Jan 13 '13 at 14:59
  • @slowdog: True, but for this purpose (ZIP archive) I don't think it matters. – Beta Jan 13 '13 at 15:11
  • 1
    @Beta: How about this: Developer A deletes a file from the `res` directory which they think is unused, runs `make`, tests the jar file, commits. Developer B makes a clean checkout of the code, runs `make`, gets a broken build. – slowdog Jan 13 '13 at 15:30
  • 1
    @slowdog: Then A broke the build (and failed to test it). The makefile can't determine whether the file is needed-- oh, wait... maybe it can... Well, anyway, a developer who has the power to remove a file (and there must be such a person) should know better than to test one set of files and then check another in; having Make detect a removal won't protect against that level of bad judgement. – Beta Jan 13 '13 at 16:42
  • 18
    @slowdog: When a file is deleted, the modified date of `res` directory will also be changed. Adding depency of `res` directory itself may solve the problem. Simply remove `-type f` from `find` command. – Haoshu Dec 22 '14 at 12:30
  • 8
    To allow spaces `filename.jar: $(shell find res -type f | sed 's/ /\\ /g')` seems to work for me – flungo May 23 '15 at 10:40
  • 1
    The `**` syntax does not exist in any version of Make that I know of. See e.g. https://www.gnu.org/software/make/manual/html_node/Wildcards.html#Wildcards – Resigned June 2023 Aug 26 '17 at 00:45
  • By the way, if you want to do this in a [pattern rule](https://www.gnu.org/software/make/manual/html_node/Pattern-Rules.html), you can use [secondary expansion](https://www.gnu.org/software/make/manual/html_node/Secondary-Expansion.html). For instance, `bin/%.jar: $$(shell find res/%)` would create a pattern rule for any `bin/.jar`, which depends on all of the files and folders (recursively) in `res/`. – Alexander Guyer May 19 '23 at 01:18