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

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)

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.

rule to run a command on newly generated files with gnumake

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.

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')

Call cmake from make to create Makefiles?

I am using cmake to build my project. For UNIX, I would like to type make from my project's root directory, and have cmake invoked to create the proper Makefiles (if they don't exist yet) and then build my project. I would like the cmake "internal" files (object files, cmake internal Makefiles, etc.) to be hidden (e.g. put in a .build directory) so it doesn't clutter my project directory.
My project has several sub-projects (in particular, a library, a user executable, and a unit test executable). I would like Makefiles (i.e. I type make and this happens) for each sub-project to execute cmake (as above) and build only that sub-project (with dependencies, so the library would be built from the executables' Makefiles, if needed). The resulting binary (.so library or executable) should be in the sub-project's directory.
I made a Makefile which does the main project bit somewhat well, though it feels somewhat hackish. I can't build specific targets using it, because my Makefile simply calls make in cmake's build directory.
Note that because the library is a sole dependency (and probably doesn't need to be build manually, and because I'm lazy) I omitted it in my Makefile.
BUILD_DIR := .build
.PHONY: all clean project-gui ${BUILD_DIR}/Makefile
all: project-gui project-test
clean:
#([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean && rm -r ${BUILD_DIR}) || echo Nothing to clean
project-gui: ${BUILD_DIR}/Makefile
#make -C ${BUILD_DIR} project-gui
#cp ${BUILD_DIR}/project-gui/project-gui $#
project-test: ${BUILD_DIR}/Makefile
#make -C ${BUILD_DIR} project-test
#cp ${BUILD_DIR}/project-test/project-test $#
${BUILD_DIR}/Makefile:
#[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
#[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CMAKE_OPTS} ..)
If it helps, here's my project structure (if this is "wrong" please tell me -- I'm still learning cmake):
project/
project/CMakeLists.txt
project/common.cmake
project/Makefile -- see Makefile above for this; should be replaced with something better, building libproject, project-gui, and project-test
project/libproject/
project/libproject/CMakeLists.txt
project/libproject/libproject.so -- after build
project/libproject/Makefile -- doesn't exist yet; should build libproject only
project/libproject/source/
project/libproject/include/
project/project-gui/
project/project-gui/CMakeLists.txt
project/project-gui/Makefile -- doesn't exist yet; should build libproject then project-gui
project/project-gui/source/
project/project-gui/include/
project/project-test/
project/project-test/CMakeLists.txt
project/project-test/Makefile -- doesn't exist yet; should build libproject then project-test
project/project-test/source/
project/project-test/include/
If you haven't caught on yet, I'm basically looking for a way to build the project and sub-projects as if cmake wasn't there: as if my project consisted of only Makefiles. Can this be done? Is the solution elegant, or messy? Should I be trying to do something else instead?
Thanks!
If cmake is generating the makefiles, you can simply include the generated makefile in the master makefile, eg
# makefile
all: # Default
include $GENERATED
$GENERATED:$CMAKEFILE
# Generate the makefile here`
The included files are generated then make is restarted with the new included files. The included files should detail the targets, etc.
You should be able to change the location of used files using the vpath directive, see e.g. the Gnu make manual,
vpath %.o project/.build
else the tedious way is to rewrite the rules making note of the necessary directory.
Ed:
Perhaps we shouldn't use a flat makefile.
Try something like:
# makefile
all: gui test
clean:
$(MAKE) -f $(GUI-MAKE) clean
$(MAKE) -f $(TEST-MAKE) clean
gui:$(GUI-MAKE)
$(MAKE) -f $(GUI-MAKE) all
$(GUI-MAKE):$(GUI-CMAKE)
# Generate
# Same for test
This should work if the $(MAKE) -f $(GUI-MAKE) all command works on the command line, and we've hidden cmake in the generating target. You would have to copy any other targets to the master makefile as well, and take care running make in parallel.
Propagating object files through should involve something like
%.o:$(GUI-MAKE)
$(MAKE) -f $(GUI-MAKE) $#
although you'll probably get errors trying to make test objects

Resources