Search code examples
shellmakefilegenerated-code

Makefile always says generated-file target is up to date, but generates target as expected


I have a script that, when run, generates a file. I want to create a Makefile to run the script to generate the file if the generated file does not exist or if the script changes.

Here's a simplified version. Let's say I have an executable script gen_test.sh like this:

#!/bin/sh

echo "This is a generated file" > genfile.foo

And a Makefile like this:

#
# Test Makefile
#

GEN_SCRIPT = $(CURDIR)/gen_test.sh
GEN_FILE = $(CURDIR)/genfile.foo

$(GEN_FILE): $(GEN_SCRIPT)
        $(shell $(GEN_SCRIPT))

.PHONY: clean
clean:
        $(RM) $(GEN_FILE)

When I run make, I see the following output:

make: '/workspace/sw/bhshelto/temp/makefile_test/genfile.foo' is up to date.

but genfile.foo does in fact get generated.

What's going on here? Why do I see the "is up to date" message, and how do I get rid of it?

Thanks!


Solution

  • When I run make, I see the following output:

    make: '/workspace/sw/bhshelto/temp/makefile_test/genfile.foo' is up to date.
    

    but genfile.foo does in fact get generated.

    As user657267 has suggested: remove the $(shell) from the recipe body.

    Assuming that the gen_test.sh doesn't produce any output (or output only on stderr), the explanation for the behavior is rather simple:

    1. The make detects that the $(GEN_FILE) needs to be rebuild and goes to invoke the rule.

    2. Before the command invocation, make performs the expansion of the variables inside the commands. Due to the (unnecessary) $(shell), the $(GEN_SCRIPT) gets invoked already during this expansion.

    3. The $(GEN_SCRIPT) does it work, but produces no output, thus $(shell $(GEN_SCRIPT)) is expanded to an empty string.

    4. Make sees that the command is empty, and thus there is no command to run.

    5. The make then again checks the files (in case the variable expansion has side-effects) and sees that the target is up-to-date. Yet, since the expanded command is empty, the make prints its generic is up to date message.