Our data files are a product not only of our text files but the
script, countwords.py, that processes the text files and creates the
data files. A change to countwords.py (e.g. to add a new column of
summary data or remove an existing one) results in changes to the
.dat files it outputs. So, let’s pretend to edit countwords.py,
using touch, and re-run Make:

$ make dats
$ touch countwords.py
$ make dats

Nothing happens! Though we’ve updated countwords.py our data files
are not updated because our rules for creating .dat files don’t
record any dependencies on countwords.py.

We need to add countwords.py as a dependency of each of our
data files also:

Dry run

make can show the commands it will execute without actually running them if we pass the -n flag:

$ touch countwords.py
$ make -n dats

This gives the same output to the screen as without the -n flag, but the commands are not actually run. Using this ‘dry-run’ mode is a good way to check that you have set up your Makefile properly before actually running the commands in it.

The following figure shows the dependencies embodied within our
Makefile, involved in building the results.txt target, after adding
countwords.py and testzipf.py as dependencies to their respective target files
(i.e. how the Makefile should look after completing the rest of the exercises
in this episode).

Why Don’t the .txt Files Depend on countwords.py?

.txt files are input files and have no dependencies. To make these
depend on countwords.py would introduce a false
dependency.

Intuitively, we should also add countwords.py as dependency for
results.txt, as the final table should be rebuilt as we remake the
.dat files. However, it turns out we don’t have to! Let’s see what
happens to results.txt when we update countwords.py:

The whole pipeline is triggered, even the creation of the
results.txt file! To understand this, note that according to the
dependency figure, results.txt depends on the .dat files. The
update of countwords.py triggers an update of the *.dat
files. Thus, make sees that the dependencies (the .dat files) are
newer than the target file (results.txt) and thus it recreates
results.txt. This is an example of the power of make: updating a
subset of the files in the pipeline triggers rerunning the appropriate
downstream steps.

Updating One Input File

What will happen if you now execute:

$ touch books/last.txt
$ make results.txt

only last.dat is recreated

all .dat files are recreated

only last.dat and results.txt are recreated

all .dat and results.txt are recreated

Solution

3. only last.dat and results.txt are recreated.

Follow the dependency tree to understand the answer(s).

testzipf.py as a Dependency of results.txt.

What would happen if you added testzipf.py as dependency of results.txt, and why?

We still have to add the testzipf.py script as dependency to
results.txt. Given the answer to the challenge above, we cannot use
$^ in the rule.
We can however move testzipf.py to be the
first dependency and then use $< to refer to it.
In order to refer to the .dat files, we can just use *.dat for now (we will
cover a better solution later on).