rule to run a command on newly generated files with gnumake - gnu-make

I am very new with make. I have a perl script that generates three latex files. I want to create a makefile that would execute the perl script and then run lualatex on the newly generates tex files. So far, I have the following:
make:
perl diff.pl
pdf:
make
$(eval LIST := $(shell ls *.tex))
lualatex $(LIST).tex
make clean
clean:
rm -rf *.log *.aux
Output:
lualatex FLAT_FLAT_AVDD.tex FLAT_FLAT_VDD.tex FLAT_FLAT_VSS.tex.tex
And I only get one pdf FLAT_FLAT_AVDD.pdf.
How can I run lualatex on all the files?
I can just declare three variables and then run make. But, how can I automate this? Is there a loop concept in make? What is a better way to achieve this with "hard-coding" the file names?
Thanks.
EDIT:
I tried to incorporate foreach.
make:
perl diff.pl
list:
$(eval LIST := $(shell ls *.tex))
pdf:
make list
$(foreach tex,$(LIST),$(lualatex $(tex)))
make clean
clean:
rm -rf *.log *.aux
and then I ran, make pdf
I got the following output in terminal.
dedehog01:tislam1:243 > make pdf
make list
make[1]: Entering directory `/home/tislam1/Documents/THESIS/Script_v0.1/BOX_approach/Modified_Layout_mesh/IR_Report_mesh/flat_flat/make'
make[1]: `list' is up to date.
make[1]: Leaving directory `/home/tislam1/Documents/THESIS/Script_v0.1/BOX_approach/Modified_Layout_mesh/IR_Report_mesh/flat_flat/make'
make clean
make[1]: Entering directory `/home/tislam1/Documents/THESIS/Script_v0.1/BOX_approach/Modified_Layout_mesh/IR_Report_mesh/flat_flat/make'
rm -rf *.log *.aux
make[1]: Leaving directory `/home/tislam1/Documents/THESIS/Script_v0.1/BOX_approach/Modified_Layout_mesh/IR_Report_mesh/flat_flat/make'

Assuming you know the names of the three .tex files in advance, you can unconditionally run the perl, then update the .tex files only if the new ones actually differ from the old ones. Make will handle this fine.
tex := 1.tex 2.tex 3.tex
intermediates := ${tex:%.tex=%-new.tex}# 1-new.tex 2-new.tex 3-new.tex
pdfs := ${intermediates:%.tex=%.pdf}# 1-new.pdf etc.
.PHONY: perl
perl: ; perl diff.pl
${intermediates}: %-new.tex: %.tex | perl
cmp -s $< $# || mv $< $#
${pdfs}: %.pdf: %.tex
lualatex $<
.PHONY: all
all: ${pdfs}
: $# Success
Let's say the perl produces 1.tex, 2.tex and 3.tex
We unconditionally run the perl producing new copies of these three files.
Then we update 1-new.tex from 1.tex, but only if the two files differ.
Make notices any files that have changed and runs lualatex as appropriate.
This is parallel safe (a good test of any makefile). Run with -j3 to get 3 copies of lualatex running at once. You do have 4 CPUs don't you?

If you want to run your perl script on each make invocation, make is not really useful. A shell script could do the same. But if you really want to put all this a makefile:
.PHONY: all clean
all:
perl diff.pl && \
for t in *.tex; do \
lualatex $$t; \
done
clean:
rm -rf *.log *.aux
Else, you must know in advance the list of LaTeX sources. And you should probably stick with make target-prerequisites philosophy, that is, express all dependencies and the corresponding recipes:
LATEXSOURCES := foo.tex bar.tex ... cuz.tex
PDFS := $(patsubst %.tex,%.pdf,$(LATEXSOURCES))
all: $(PDFS)
$(LATEXSOURCES): diff.pl
perl diff.pl
$(PDFS): %.pdf: %.tex
lualatex $<
clean:
rm -rf *.log *.aux
But, as bobbogo noticed, with this second option, the perl script will be run as many times as there are LaTeX source files. A pattern rule solves this:
LATEXSOURCES := foo.tex bar.tex ... cuz.tex
PDFS := $(patsubst %.tex,%.pdf,$(LATEXSOURCES))
all: $(PDFS)
$(LATEXSOURCES): %.tex: diff.pl
#echo "Rebuilding $(LATEXSOURCES)"
perl diff.pl
$(PDFS): %.pdf: %.tex
lualatex $<
clean:
rm -rf *.log *.aux
Now we have a true solution that:
rebuilds the LaTeX source files only if one is missing or if the perl script changed since they were last built,
executes the perl script only once to build all LaTeX source files,
expresses all dependencies between the various files.
There is still a problem, however: if only one LaTeX source file is missing and the others are up-to-date, the perl script will be run, all LaTeX source files will be rebuilt and their last modification time will thus be changed. Only the missing one will be compiled but, on the next make invocation, the others will also be re-compiled, which is a waste. bobbogo's proposal of using intermediate LaTeX sources solves this.

Related

Copy a file using makefile

I created the following makefile to generate a.pdf and then copy it to b.pdf.
all:
arara a.tex
rm *.dvi *.aux *.log
cp a.pdf b.pdf
The first two commands run correctly: meaning a file named a.pdf is generated, and the temporary *.dvi, *.aux, *.log files are removed.
But the cp command does not run: meaning b.pdf is not created. Any idea why? I tried cp -f as well.
(arara is a utility to compile a tex file and generate a pdf file, its details are not important for this question)
The error turned out to be in the rm command and not in the cp command. One of the extensions *.aux did not exist and so rm threw an error. I resolved it by adding -f to the rm command.

Makefile to render all targets of all .Rmd files in directory

My aim is to have a universal Makefile which I can copy into each directory where I have an RMD file, which will, upon calling make in this directory, render all targets defined in all .Rmd files in this directory.
The Makefile below works for only renders the last file as expected. I am sure I am doing something simple wrong.
How do I have to modify the Makefile so that it does what it is supposed to do?
Also: when I run make a second time, all files are generated again, although no SOURCE files changed.
I have the following Makefile:
SOURCES=$(shell find . -name "*.Rmd")
TARGETS_pdf=$(SOURCES:%.Rmd=%.pdf)
TARGETS_html=$(SOURCES:%.Rmd=%.html)
TARGETS_nb_html=$(SOURCES:%.Rmd=%.nb.html)
TARGETS_docx=$(SOURCES:%.Rmd=%.docx)
default: $(SOURCES)
$(info Generating defined targets from $(SOURCES))
#echo "$< -> $#"
#Rscript -e "rmarkdown::render('$<', output_format = 'all')"
clean:
rm -rf $(TARGETS_pdf)
rm -rf $(TARGETS_html)
rm -rf $(TARGETS_nb_html)
rm -rf $(TARGETS_docx)
Thanks.
When you run make it executes the first rule it finds. In your case it is default. It checks then if this file exists. If it does not, the script is run, which is supposed to generate the target file (default). Your script does not do that. That is why next time make runs, it starts all over again. If the file exists, the script does not need to be run.
What you could do is this:
SOURCES=$(shell find . -name "*.Rmd")
TARGET = $(SOURCES:%.Rmd=%.pdf) $(SOURCES:%.Rmd=%.html) $(SOURCES:%.Rmd=%.nb.html) $(SOURCES:%.Rmd=%.docx)
%.docx %.nb.html %.html %.pdf: %.Rmd
Rscript -e "rmarkdown::render('$<', output_format = 'all')"
default: $(TARGET)
clean:
rm -rf $(TARGET)

Makefile run an action before everything

For building my target I have a list of prerequisites contained in a file list.txt and a script for generating this file generate-list.sh.
I need the script to be executed as first thing every time I invoke the make in order to have the list.txt updated and to give ti make the right list of prerequisites.
prebuild:
touch list.txt
.SECONDEXPANSION:
exe: prebuild $$(shell cat list.txt)
touch exe
<files in list.txt>:
<rules for generating these files>
In this way when I run make I first get an error from cat saying that list.txt does not exist, then list.txt is generated but since the cat failed the prerequisites contained in list.txt are not generated.
One method you could use, given that generate_list.sh must be executed at the very start every time, would be to explicitly execute it using the shell function. This would mean altering your makefile to something like
$(shell ./generate_list.sh > /dev/null)
.SECONDEXPANSION:
exe: $(shell cat list.txt)
touch exe
#echo $?
<files in list.txt>:
<rules for generating these files>
Executing this makefile produces
$ make
touch exe
deps.c test.c
where my generate_list.sh file contains
#!/bin/bash
touch test.c deps.c
echo deps.c test.c > list.txt
echo 'Created prerequisites list.'
Notes
/dev/null is included in $(shell ./generate_list.sh > /dev/null) incase your generate_list.sh produces an output as this would cause an error in make of
$ make
GNUmakefile:1: *** missing separator. Stop.
otherwise.
#echo $? shows that all of the prerequisites in list.txt are now included as prerequisites of exe.
Alternate Method Based on Auto Dependency Generation
What you are attempting to do is very similar to automatic dependency generation which can be accomplished using the -include directive in make. For future usage you may want to consider going down this route and altering your generate_list.sh script to create a makefile that can be included in your main makefile.

Unix command line: find different behaviour from dir to dir

I'm using a find command in a makefile like so:
CC = clang++
CODE = $(shell find . -name *.cpp) #find command here !!!!!!
EXEC = tcr_translator
.PHONY: all clean
all: $(OBJECT)
clear
$(CC) $(CODE) -o $(EXEC) -I src/
clean:
rm $(EXEC)
run: all
./tcr_translator
So, this find command list all the cpp file to compile, makes it easier than to hard-write them in my makefile. This worked perfectly for my last project, so I took the same makefile for my new project. My problem, now the find command (which is, char for char, the same command) won't look into my subdirectories.
So if I have .cpp in a src/ subfolder, it won't find them.
Why would such a command work in a certain directory and not in an other. I'm really at a loss here :(
You should quote the *.cpp to avoid it from being expanded by shell before passed into find.
CODE = $(shell find . -name '*.cpp')

Makefile gives strange error while compiling markdown file into .docx file

I am having problems with makefile for creating docx file from rmd using R.
Here is my make file, which patially works fine, except last part:
all: ibn_paper.pdf; cabg_n_cor_draft.docx
ibn_paper.md:
Rscript -e "library(knitr); knit('ibn_paper.Rmd')"
ibn_paper.pdf: ibn_paper.md
pandoc -H format.sty -V fontsize=12pt --bibliography ibn_refs.bib ibn_paper.md -o ibn_paper.pdf
cabg_n_cor_draft.docx: ibn_paper.md
pandoc -s -S -V fontsize=12pt --bibliography ibn_refs.bib ibn_paper.md -o
cabg_n_cor_draft.docx
clean:
#-rm -r *.md
And all this code works and creates at the end this error:
make: cabg_n_cor_draft.docx: No such file or directory
make: *** [all] Error 1
Exited with status 2.
How can I solve this problem?
Many thanks beforehand.
Try to remove semicolon in the first line. You probably wanted to let all depend on the two files. But semicolon separates recipe from list of prerequisites.
Look at makefile rule syntax

Resources