Reputation: 775
I am trying to compile some latex that has snippets of python code and the output of those snippets. I need the document to be always updated with the last changes made in the snippets and in their outputs, so the idea is maintain a makefile that could monitor this changes and generate the updated outputs.
So if I modify the file a/11.py
, I want make
to execute it to generate a new output a/11.out
.
This is my makefile
DOC=myPdf
STY=st
PY_DIR=a/
TEX=pdflatex -shell-escape -interaction=batchmode -file-line-error
$(DOC).pdf: $(PY_DIR)11.out $(PY_DIR)12.out $(DOC).tex $(STY).sty
$(TEX) $(DOC).tex
$(PY_DIR)11.out:
$(cd PY_DIR && python3 11.py > 11.out)
$(PY_DIR)12.out:
$(cd PY_DIR && python3 12.py > 12.out)
.PHONY: clean
clean:
rm *.aux *.log > /dev/null 2>&1
Even when the file a/11.out
doesn't exist, and I instruct make a/11.out
make says: make: 'a/11.out' is up to date.
(I am still learning make, so I probably have more mistakes).
$(MAKE)
, I cannot use it.Thank you for your time :)
This is my new version, based in the answer of Renaud (thanks for your help), some python scripts are intended to output text (xxxt.py), and others to plot images (xxxi.py), so there is no redirection for them:
DOC :=myPdf
STY :=st
PY_DIR :=a/
TEX :=pdflatex -shell-escape -interaction=batchmode -file-line-error
PYS := $(wildcard $(PY_DIR)*.py)
OUTS := $(patsubst %.py,%.out,$(PYS))
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(STY).sty $(OUTS)
$(TEX) $<
$(PY_DIR)%.out: $(PY_DIR)%t.py
cd $(PY_DIR) && python3 $*t.py > $*.out
$(PY_DIR)%.png: $(PY_DIR)%i.py
cd $(PY_DIR) && python3 $*i.py
clean:
rm *.aux *.log > /dev/null 2>&1
The directory looks like this:
./st.sty
./myPdf.tex
./myPdf.pdf
./a/11t.py
./a/11.out
./a/12i.py
./a/12.png
./a/21t.py
./a/...
However, now right after modifying myPdf.tex, make says make: Nothing to be done for 'all'.
What am I doing wrong?
Upvotes: 0
Views: 783
Reputation: 28910
Your recipes are wrong. Make expands the recipes before passing them to the shell. As there is no make variable named cd PY_DIR && python3 11.py > 11.out
, $(cd PY_DIR && python3 11.py > 11.out)
expands as the empty string and make considers that there is nothing to do for $(PY_DIR)11.out
. Just write your recipes as plain shell (and fix the other bug with the unexpanded PY_DIR
):
$(PY_DIR)11.out:
cd $(PY_DIR) && python3 11.py > 11.out
$(PY_DIR)12.out:
cd $(PY_DIR) && python3 12.py > 12.out
Note: if you want make to re-run the recipes when your python scripts change you should let him know that the output files depend on the python scripts. The best is probably to use a pattern rule instead of one specific rule per file:
$(PY_DIR)%.out: $(PY_DIR)%.py
cd $(PY_DIR) && python3 $*.py > $*.out
($*
is a make automatic variable, it expands as the stem of the pattern).
A few more improvements:
xx.tex -> xx.pdf
process. And use another make automatic variable for it: $<
that expands as the first prerequisite.DOC := myPdf
STY := st
PY_DIR := a/
TEX := pdflatex -shell-escape -interaction=batchmode -file-line-error
PYS := $(wildcard $(PY_DIR)*.py)
OUTS := $(patsubst %.py,%.out,$(PYS))
.PRECIOUS: $(OUTS)
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(OUTS) $(STY).sty
$(TEX) $<
$(PY_DIR)%.out: $(PY_DIR)%.py
cd $(PY_DIR) && python3 $*.py > $*.out
.PHONY: clean
clean:
rm *.aux *.log > /dev/null 2>&1
Note: I declared $(OUTS)
as precious such that make does not delete them when it is done with the building of $(DOC).pdf
.
Update with the new specifications and separated python scripts for xx.out
and xx.png
production:
DOC := myPdf
STY := st
PY_DIR := a
TEX := pdflatex -shell-escape -interaction=batchmode -file-line-error
PYTS := $(wildcard $(PY_DIR)/*t.py)
PYIS := $(wildcard $(PY_DIR)/*i.py)
OUTS := $(patsubst $(PY_DIR)/%t.py,$(PY_DIR)/%.out,$(PYTS))
PNGS := $(patsubst $(PY_DIR)/%i.py,$(PY_DIR)/%.png,$(PYIS))
.PRECIOUS: $(OUTS) $(PNGS)
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(STY).sty $(OUTS) $(PNGS)
$(TEX) $<
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(PY_DIR) && python3 $*t.py > $*.out
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(PY_DIR) && python3 $*i.py
clean:
rm -f *.aux *.log > /dev/null 2>&1
Notes:
PY_DIR
such that, when used in other parts of the Makefile, it is clear that it is a directory path. Just a matter of taste, I guess.-f
option to your clean
recipe such that it doesn't fail if the files to delete do not exist.Update:
As noted by MadScientist in a comment, using $*
is less generic than referring to the target ($@
) and the prerequisite ($<
). But as we are operating not directly on them but on their directory ($(PY_DIR)
) and base file names (xx[it].py
, xx.out
, xx.png
), switching from $*
to other, more generic, automatic variables is not that simple.
But make has some more tricks that can help here: $@
, $<
... have variants ($(@F)
, $(@D)
...) that expand to just the directory part or the file part. Note that, according the GNU make manual:
These variants are semi-obsolete in GNU make since the functions
dir
andnotdir
can be used to get a similar effect.
Anyway, if we wanted to avoid $*
here is what we could use instead:
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(@D) && python3 $(<F) > $(@F)
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(@D) && python3 $(<F)
Or (modern version):
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(dir $@) && python3 $(notdir $<) > $(notdir $@)
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(dir $@) && python3 $(notdir $<)
Upvotes: 1