Contents of Makefile:
# regenerate results
results/moby_dick.csv : data/moby_dick.txt
python src/countwords.py \
data/moby_dick.txt > results/moby_dick.csv
# indicates a commenttarget : prerequisite/) splits lineRun using command make
results/moby_dick.csv doesn't exist, Make runs recipe to create itdata/moby_dick.txt is newer than results/moby_dick.csv, Make runs recipe to update itresults/moby_dick.csv is newer than its prerequisite, nothing happensContents of Makefile:
# regenerate results for "Moby Dick"
results/moby_dick.csv : data/moby_dick.txt
python src/countwords.py \
data/moby_dick.txt > results/moby_dick.csv
# regenerate results for "Jane Eyre"
results/jane_eyre.csv : data/jane_eyre.txt
python src/countwords.py \
data/jane_eyre.txt > results/jane_eyre.csv
By default, Make only attempts to update the first target (default target)
Could specify target directly: make results/jane_eyre.csv
Better, create "phony target" and place at top: all
# regenerate all results
all : results/moby_dick.csv results/jane_eyre.csv
Then type make all
By convention a clean target provides rules to remove
results/generated outputs
# remove all generated files
clean :
rm -rf results/*.csv
Then type make clean. Safer than manually typing!
Problem if file/directory named "clean". Avoid this by explicitly telling phony targets at top of file:
.PHONY : all clean
The results also depend on the programs used to generate them, so add to prerequisites:
# regenerate results for "Moby Dick"
results/moby_dick.csv : data/moby_dick.txt src/countwords.py
python src/countwords.py \
data/moby_dick.txt > results/moby_dick.csv
# regenerate results for "Jane Eyre"
results/jane_eyre.csv : data/jane_eyre.txt src/countwords.py
python src/countwords.py \
data/jane_eyre.txt > results/jane_eyre.csv
.PHONY : all clean
COUNT=src/countwords.py
RUN_COUNT=python $(COUNT)
# regenerate all results
all : results/moby_dick.csv results/jane_eyre.csv
# regenerate results for "Moby Dick"
results/moby_dick.csv : data/moby_dick.txt $(COUNT)
$(RUN_COUNT) data/moby_dick.txt > results/moby_dick.csv
# regenerate results for "Jane Eyre"
results/jane_eyre.csv : data/jane_eyre.txt $(COUNT)
$(RUN_COUNT) data/jane_eyre.txt > results/jane_eyre.csv
# remove all generated files
clean :
rm -f results/*.csv
Automatic variable for target of the rule: $@
# regenerate results for "Moby Dick"
results/moby_dick.csv : data/moby_dick.txt $(COUNT)
$(RUN_COUNT) data/moby_dick.txt > $@
The first prerequisite of the rule: $<
# regenerate results for "Moby Dick"
results/moby_dick.csv : data/moby_dick.txt $(COUNT)
$(RUN_COUNT) $< > $@
Also: all prerequisites of the rule: $^
Create pattern rule using wildcard: %
results/%.csv : data/%.txt $(COUNT)
$(RUN_COUNT) $< > $@
So full Makefile is:
.PHONY : all clean
COUNT=src/countwords.py
RUN_COUNT=python $(COUNT)
# regenerate all results
all : results/moby_dick.csv results/jane_eyre.csv \
results/time_machine.csv
# regenerate results for any book
results/%.csv : data/%.txt $(COUNT)
$(RUN_COUNT) $< > $@
# remove all generated files
clean :
rm -f results/*.csv
Use variable to list all results files present:
RESULTS=results/*.csv
all : $(RESULTS)
But, only works if results already exist. Instead, use list in data/ directory.
DATA=$(wildcard data/*.txt)
Use pattern substitution to create corresponding output files:
RESULTS=$(patsubst data/%.txt,results/%.csv,$(DATA))
Use settings target to print variables, using
@ to avoid repeating command in output:
# ... rest of Makefile
# show variables' values
settings :
@echo COUNT: $(COUNT)
@echo DATA: $(DATA)
@echo RESULTS: $(RESULTS)
Further streamlining: remove RUN_COUNT variable:
# regenerate results for any book
results/%.csv : data/%.txt $(COUNT)
python $(COUNT) $< > $@
Since all depends on $(RESULTS)
we can regenerate in one step:
make clean
make
Create a phony target help to print commands:
.PHONY: all clean help settings
# ... other definitions ...
# show help
help :
@echo "all : regenerate all results."
@echo "results/*.csv : regenerate result for any book."
@echo "clean : remove all generated files."
@echo "settings : show variables' values."
@echo "help : show this message."
Problem with this? Requires manual updates.
Use ## to mark lines to display and
grep to pull lines:
.PHONY: all clean help settings
COUNT=src/countwords.py
DATA=$(wildcard data/*.txt)
RESULTS=$(patsubst data/%.txt,results/%.csv,$(DATA))
## all : regenerate all results.
all : $(RESULTS)
## results/%.csv : regenerate result for any book.
results/%.csv : data/%.txt $(COUNT)
python $(COUNT) $< > $@
## clean : remove all generated files.
clean :
rm -f $(RESULTS)
## settings : show variables' names
settings :
@echo COUNT: $(COUNT)
@echo DATA: $(DATA)
@echo RESULTS: $(RESULTS)
## help : show this message
help :
@grep '^##' ./Makefile
.PHONY: all paper clean help settings
COUNT=src/countwords.py
DATA=$(wildcard data/*.txt)
RESULTS=$(patsubst data/%.txt,results/%.csv,$(DATA))
## all : regenerate paper and all results.
all : paper.pdf $(RESULTS)
## results/%.csv : regenerate result for any book.
results/%.csv : data/%.txt $(COUNT)
python $(COUNT) $< > $@
## paper.pdf : regenerate paper.
paper.pdf : paper.tex paper.bib $(RESULTS)
latexmk -pdf $<
## clean : remove all generated files.
clean :
rm -f $(RESULTS)
latexmk -c
## settings : show variables' names
settings :
@echo COUNT: $(COUNT)
@echo DATA: $(DATA)
@echo RESULTS: $(RESULTS)
## help : show this message
help :
@grep '^##' ./Makefile