How to call Makefile recipe/rule multiple times?

3k views Asked by At

With the following example:

.PHONY: hook1 hook2

# Default target
all: hook1 hook2 hook1
    echo "Calling all"

hook1:
    echo "Calling hook1"

hook2:
    echo "Calling hook2"

I got the output:

$ make
echo "Calling hook1"
Calling hook1
echo "Calling hook2"
Calling hook2
echo "Calling all"
Calling all

But I would expect it to call the rule hook1 two times. This is because on Latex, I need to call the same program with the same command line, multiple times. Then I would like to reuse the make rules. For example, I would expect the above minimal code to be ran as:

$ make
echo "Calling hook1"
Calling hook1
echo "Calling hook2"
Calling hook2
echo "Calling hook1"
Calling hook1
echo "Calling all"
Calling all

Which call the same rule twice. This is code bellow my full main Makefile code, if anyone is interested. I tried to create the dummy rules pdflatex_hook1 and pdflatex_hook2 so I could do the call hierarchy pdflatex_hook biber_hook pdflatex_hook, but this do not fooled make and it still ignoring my last call to pdflatex_hook2:

#!/usr/bin/make -f
# https://stackoverflow.com/questions/7123241/makefile-as-an-executable-script-with-shebang
ECHOCMD:=/bin/echo -e

# The main latex file
THESIS_MAIN_FILE = modelomain.tex

# This will be the pdf generated
THESIS_OUTPUT_NAME = thesis

# This is the folder where the temporary files are going to be
CACHE_FOLDER = setup/cache

# Find all files ending with `main.tex`
LATEX_SOURCE_FILES := $(wildcard *main.tex)

# Create a new variable within all `LATEX_SOURCE_FILES` file names ending with `.pdf`
LATEX_PDF_FILES := $(LATEX_SOURCE_FILES:.tex=.pdf)


# GNU Make silent by default
# https://stackoverflow.com/questions/24005166/gnu-make-silent-by-default
MAKEFLAGS += --silent
.PHONY: clean pdflatex_hook1 pdflatex_hook2 %.pdf %.tex

# How do I write the 'cd' command in a makefile?
# http://stackoverflow.com/questions/1789594/how-do-i-write-the-cd-command-in-a-makefile
.ONESHELL:

# Default target
all: biber


##
## Usage:
##   make <target>
##
## Targets:
##   biber             build the main file with bibliography pass
##   pdflatex          build the main file with no bibliography pass
##

# Print the usage instructions
# https://gist.github.com/prwhite/8168133
help:
    @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'


# Where to find official (!) and extended documentation for tex/latex's commandline options (especially -interaction modes)?
# https://tex.stackexchange.com/questions/91592/where-to-find-official-and-extended-documentation-for-tex-latexs-commandlin
PDF_LATEX_COMMAND = pdflatex --time-statistics --synctex=1 -halt-on-error -file-line-error
LATEX = $(PDF_LATEX_COMMAND)\
--interaction=batchmode\
-jobname="$(THESIS_OUTPUT_NAME)"\
-output-directory="$(CACHE_FOLDER)"\
-aux-directory="$(CACHE_FOLDER)"


# Run pdflatex, biber, pdflatex
biber: biber_hook pdflatex_hook2

    # Calculate the elapsed seconds and print them to the screen
    . ./setup/scripts/timer_calculator.sh
    showTheElapsedSeconds "$(current_dir)"

# Internally called rule which does not attempt to show the elapsed time
biber_hook: pdflatex_hook1

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    biber "$(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME)"


# This rule will be called for every latex file and pdf associated
pdflatex: $(LATEX_PDF_FILES)

    # Calculate the elapsed seconds and print them to the screen
    . ./setup/scripts/timer_calculator.sh
    showTheElapsedSeconds "$(current_dir)"

# Not show the elapsed time when called internally
pdflatex_hook1: $(LATEX_PDF_FILES)
pdflatex_hook2: $(LATEX_PDF_FILES)


%.pdf: %.tex

    # Start counting the compilation time and import its shell functions
    . ./setup/scripts/timer_calculator.sh

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    @$(LATEX) $<
    cp $(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME).pdf $(current_dir)/$(THESIS_OUTPUT_NAME).pdf

Here when I call the rule make biber I got the output:

$ make biber
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (MiKTeX 2.9.6400)
entering extended mode
gross execution time: 62751 ms

user mode: 58406 ms, kernel mode: 1359 ms, total: 59765
INFO - This is Biber 2.7
INFO - Logfile is 'setup/cache/thesis.blg'
INFO - Reading 'setup/cache/thesis.bcf'
INFO - Found 14 citekeys in bib section 0
INFO - Processing section 0
INFO - Looking for bibtex format file 'modeloreferences.bib' for section 0
INFO - Decoding LaTeX character macros into UTF-8
INFO - Found BibTeX data source 'modeloreferences.bib'
INFO - Overriding locale 'pt-BR' defaults 'normalization = NFD' with 'normalization = prenormalized'
INFO - Overriding locale 'pt-BR' defaults 'variable = shifted' with 'variable = non-ignorable'
INFO - Sorting list 'nty/global/' of type 'entry' with scheme 'nty' and locale 'pt-BR'
INFO - No sort tailoring available for locale 'pt-BR'
INFO - Writing 'setup/cache/thesis.bbl' with encoding 'UTF-8'
INFO - Output to setup/cache/thesis.bbl
Could not calculate the seconds to run

Which is missing the second call to the rule pdflatex_hook2, only the first call to pdflatex_hook1 is being performed.`

I already know about latexmk and use it, but for biber above I would like to do these calls as they are. For latexmk I use this recipe/rule:

thesis: $(THESIS_MAIN_FILE)

    # Start counting the compilation time and import its shell functions
    . ./setup/scripts/timer_calculator.sh

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    # What is the difference between “-interaction=nonstopmode” and “-halt-on-error”?
    # https://tex.stackexchange.com/questions/258814/what-is-the-difference-between-interaction-nonstopmode-and-halt-on-error
    #
    # What reasons (if any) are there for compiling in interactive mode?
    # https://tex.stackexchange.com/questions/25267/what-reasons-if-any-are-there-for-compiling-in-interactive-mode
    latexmk \
    -pdf \
    -silent \
    -jobname="$(THESIS_OUTPUT_NAME)" \
    -output-directory="$(CACHE_FOLDER)" \
    -aux-directory="$(CACHE_FOLDER)" \
    -pdflatex="$(PDF_LATEX_COMMAND) --interaction=batchmode" \
    -use-make $(THESIS_MAIN_FILE)

    # Copy the generated PDF file from the cache folder
    cp $(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME).pdf $(current_dir)/$(THESIS_OUTPUT_NAME).pdf

    # Calculate the elapsed seconds and print them to the screen
    showTheElapsedSeconds "$(current_dir)"

Related questions:

  1. Change a make variable, and call another rule, from a recipe in same Makefile?
  2. How to manually call another target from a make target?
  3. multiple targets from one recipe and parallel execution
2

There are 2 answers

0
David White On BEST ANSWER

To answer your initial question:

.PHONY: hook1 hook2

# Default target
all: hook1a hook2 hook1b
    echo "Calling all"

hook1a hook1b:
    echo "Calling hook1"

hook2:
    echo "Calling hook2"

Produces the following output:

echo "Calling hook1"
Calling hook1
echo "Calling hook2"
Calling hook2
echo "Calling hook1"
Calling hook1
echo "Calling all"
Calling all

As illustrated in make recipe execute twice

0
Evandro Coan On

Based on @David White answer I fixed my main script with the double recursion:

#!/usr/bin/make -f
# https://stackoverflow.com/questions/7123241/makefile-as-an-executable-script-with-shebang
ECHOCMD:=/bin/echo -e

# The main latex file
THESIS_MAIN_FILE = modelomain.tex

# This will be the pdf generated
THESIS_OUTPUT_NAME = thesis

# This is the folder where the temporary files are going to be
CACHE_FOLDER = setup/cache

# Find all files ending with `main.tex`
LATEX_SOURCE_FILES := $(wildcard *main.tex)

# Create a new variable within all `LATEX_SOURCE_FILES` file names ending with `.pdf`
LATEX_PDF_FILES := $(LATEX_SOURCE_FILES:.tex=.pdf)


# GNU Make silent by default
# https://stackoverflow.com/questions/24005166/gnu-make-silent-by-default
MAKEFLAGS += --silent
.PHONY: clean biber pdflatex_hook1 pdflatex_hook2

# How do I write the 'cd' command in a makefile?
# http://stackoverflow.com/questions/1789594/how-do-i-write-the-cd-command-in-a-makefile
.ONESHELL:

# Default target
all: thesis


##
## Usage:
##   make <target>
##
## Targets:
##   all        call the `thesis` make rule
##   biber      build the main file with bibliography pass
##   latex      build the main file with no bibliography pass
##   thesis     completely build the main file with minimum output logs
##   verbose    completely build the main file with maximum output logs
##

# Print the usage instructions
# https://gist.github.com/prwhite/8168133
help:
    @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'


# Where to find official (!) and extended documentation for tex/latex's commandline options (especially -interaction modes)?
# https://tex.stackexchange.com/questions/91592/where-to-find-official-and-extended-documentation-for-tex-latexs-commandlin
PDF_LATEX_COMMAND = pdflatex --time-statistics --synctex=1 -halt-on-error -file-line-error
LATEX = $(PDF_LATEX_COMMAND)\
--interaction=batchmode\
-jobname="$(THESIS_OUTPUT_NAME)"\
-output-directory="$(CACHE_FOLDER)"\
-aux-directory="$(CACHE_FOLDER)"


# Run pdflatex, biber, pdflatex
biber: start_timer pdflatex_hook1 biber_hook pdflatex_hook2

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    # Copies the PDF to the current folder
    cp $(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME).pdf $(current_dir)/$(THESIS_OUTPUT_NAME).pdf

    # Calculate the elapsed seconds and print them to the screen
    . ./setup/scripts/timer_calculator.sh
    showTheElapsedSeconds "$(current_dir)"


start_timer:

    # Start counting the elapsed seconds to print them to the screen later
    . ./setup/scripts/timer_calculator.sh


# Internally called rule which does not attempt to show the elapsed time
biber_hook:

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    # Call biber to process the bibliography
    biber "$(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME)"


# How to call Makefile recipe/rule multiple times?
# https://stackoverflow.com/questions/46135614/how-to-call-makefile-recipe-rule-multiple-times
pdflatex_hook1 pdflatex_hook2:

    @$(LATEX) $(LATEX_SOURCE_FILES)


# This rule will be called for every latex file and pdf associated
latex: $(LATEX_PDF_FILES)

    # Calculate the elapsed seconds and print them to the screen
    . ./setup/scripts/timer_calculator.sh
    showTheElapsedSeconds "$(current_dir)"


# Dynamically generated recipes for all PDF and latex files
%.pdf: %.tex

    # Start counting the compilation time and import its shell functions
    . ./setup/scripts/timer_calculator.sh

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    @$(LATEX) $<
    cp $(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME).pdf $(current_dir)/$(THESIS_OUTPUT_NAME).pdf


thesis: $(THESIS_MAIN_FILE)

    # Start counting the compilation time and import its shell functions
    . ./setup/scripts/timer_calculator.sh

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    # What is the difference between “-interaction=nonstopmode” and “-halt-on-error”?
    # https://tex.stackexchange.com/questions/258814/what-is-the-difference-between-interaction-nonstopmode-and-halt-on-error
    #
    # What reasons (if any) are there for compiling in interactive mode?
    # https://tex.stackexchange.com/questions/25267/what-reasons-if-any-are-there-for-compiling-in-interactive-mode
    latexmk \
    -pdf \
    -silent \
    -jobname="$(THESIS_OUTPUT_NAME)" \
    -output-directory="$(CACHE_FOLDER)" \
    -aux-directory="$(CACHE_FOLDER)" \
    -pdflatex="$(PDF_LATEX_COMMAND) --interaction=batchmode" \
    -use-make $(THESIS_MAIN_FILE)

    # Copy the generated PDF file from the cache folder
    cp $(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME).pdf $(current_dir)/$(THESIS_OUTPUT_NAME).pdf

    # Calculate the elapsed seconds and print them to the screen
    showTheElapsedSeconds "$(current_dir)"


verbose: $(THESIS_MAIN_FILE)

    # Start counting the compilation time and import its shell functions
    . ./setup/scripts/timer_calculator.sh

    # Creates the shell variable `current_dir` within the current folder path
    $(eval current_dir := $(shell pwd)) echo $(current_dir) > /dev/null

    # What is the difference between “-interaction=nonstopmode” and “-halt-on-error”?
    # https://tex.stackexchange.com/questions/258814/what-is-the-difference-between-interaction-nonstopmode-and-halt-on-error
    #
    # What reasons (if any) are there for compiling in interactive mode?
    # https://tex.stackexchange.com/questions/25267/what-reasons-if-any-are-there-for-compiling-in-interactive-mode
    latexmk \
    -pdf \
    -jobname="$(THESIS_OUTPUT_NAME)" \
    -output-directory="$(CACHE_FOLDER)" \
    -aux-directory="$(CACHE_FOLDER)" \
    -pdflatex="$(PDF_LATEX_COMMAND) --interaction=nonstopmode" \
    -use-make $(THESIS_MAIN_FILE)

    # Copy the generated PDF file from the cache folder
    cp $(CACHE_FOLDER)/$(THESIS_OUTPUT_NAME).pdf $(current_dir)/$(THESIS_OUTPUT_NAME).pdf

    # Calculate the elapsed seconds and print them to the screen
    showTheElapsedSeconds "$(current_dir)"


# Using Makefile to clean subdirectories
# https://stackoverflow.com/questions/26007005/using-makefile-to-clean-subdirectories
#
# Exclude directory from find . command
# https://stackoverflow.com/questions/4210042/exclude-directory-from-find-command
GARBAGE_TYPES := "*.gz(busy)" *.aux *.log *.pdf *.aux *.bbl *.log *.out *.toc *.dvi *.blg\
*.synctex.gz *.fdb_latexmk *.fls *.lot *.lol *.lof *.idx

DIRECTORIES_TO_CLEAN  := $(shell /bin/find -not -path "./**.git**" -not -path "./pictures**" -type d)
GARBAGE_TYPED_FOLDERS := $(foreach DIR, $(DIRECTORIES_TO_CLEAN), $(addprefix $(DIR)/,$(GARBAGE_TYPES)))

clean:
    rm -rfv $(GARBAGE_TYPED_FOLDERS)


# veryclean:
#   git clean -dxf