Makefile for compiling Markdown files to a single PDF - makefile

I am trying to use a Makefile for compiling a PDF when any of a number of Markdown files change:
# Compile report
source := draft
output := dist
sources := $(wildcard $(source)/*.md)
objects := $(patsubst %.md,%.pdf,$(subst$(source),$(output),$(sources)))
all: $(objects)
report-print.md: $(source)/%.md
cat draft/*.md | pandoc \
--variable geometry:a4paper \
--number-sections \
--toc \
--f markdown \
-s \
-o dist/report-print.pdf \
.PHONY : clean
clean:
rm -f $(output)/*.pdf
I get an error:
make: *** No rule to make target `dist/01-title.pdf', needed by `all'. Stop.
The file draft/01-title.md is one of of the source files.

You don't have a rule for creating one .pdf file from one .md file. Which is fine, because that's not what you want to do. You want to create a single pdf file from all the .md files (as I understand it). So, ditch all the objects stuff; you don't need to create all those individual pdf files.
There are a number of other minor problems: you aren't creating the same filename as your target (report-print.md vs. $(output)/report-print.pdf), you should use automatic variables, etc.)
Your makefile will simply be:
source := draft
output := dist
sources := $(wildcard $(source)/*.md)
all: $(output)/report-print.pdf
$(output)/report-print.pdf: $(sources)
cat $^ | pandoc \
--variable geometry:a4paper \
--number-sections \
--toc \
--f markdown \
-s \
-o $#
.PHONY : clean
clean:
rm -f $(output)/*.pdf

Related

Makefile does not invoke action after touching the target file

Here is my Makefile:
NAME := libftprintf.a
LIB := ar rcs
CC := gcc
CFLAGS := -Wall -Wextra -Werror
SRCS_DIR := ./sources/
HDRS_DIR := ./headers/
OBJS_DIR := ./objectives/
SRCS_FILES := ft_conv.c \
ft_eval_hex.c \
ft_eval_number.c \
ft_eval_string.c \
ft_parsers.c \
ft_strs_join.c \
ft_eval_char.c \
ft_eval_int.c \
ft_eval_percent.c \
ft_handler.c \
ft_printf.c
HDRS_FILES := ft_conv.h \
ft_eval_hex.h \
ft_eval_number.h \
ft_eval_string.h \
ft_parsers.h \
ft_strs_join.h \
ft_eval_char.h \
ft_eval_int.h \
ft_eval_percent.h \
ft_handler.h \
ft_printf.h
OBJS_FILES := $(SRCS_FILES:.c=.o)
SRCS := $(addprefix $(SRCS_DIR),$(SRCS_FILES))
HDRS := $(addprefix $(HDRS_DIR),$(HDRS_FILES))
OBJS := $(addprefix $(OBJS_DIR),$(OBJS_FILES))
LIBFT_DIR := ./libft/
LIBFT_NAME := libft.a
LIBFT := $(addprefix $(LIBFT_DIR),$(LIBFT_NAME))
RM := rm -rf
all: $(NAME)
bonus: all
bonus_one: all
bonus_two: all
$(NAME): $(OBJS)
$(LIB) $(NAME) $(OBJS) $(LIBFT)
$(OBJS_DIR)%.o: $(SRCS_DIR)%.c $(HDRS) Makefile | $(OBJS_DIR) subsystem
$(CC) $(CFLAGS) -c $< -o $# -I $(HDRS_DIR) -I $(LIBFT_DIR)
subsystem:
#$(MAKE) -C $(LIBFT_DIR)
$(OBJS_DIR):
mkdir $(OBJS_DIR)
clean:
#$(MAKE) -C $(LIBFT_DIR) clean
$(RM) $(OBJS_DIR)
fclean: clean
#$(MAKE) -C $(LIBFT_DIR) fclean
$(RM) $(NAME)
re: fclean all
.PHONY: all subsystem bonus bonus_one bonus_two clean fclean re
The problem is so: if I do make all, then touch libftprintf.a I would expect that make all will rebuild libftprintf.a since it was changed and target all depends on that file. However, make does nothing and I can't understand this behavior.
Also, there is one more minor issue: during make all I create a directory objectives where I store all .o files. Calling make fclean and make all entirely rebuilds the target, however calling make re results in an error:
rm -rf ./objectives/
rm -rf *libft objectives here*
rm -rf libft.a
rm -rf libftprintf.a
make: *** No rule to make target `objectives', needed by `objectives/ft_conv.o'. Stop.
If I call make re right after this error, the target builds as always. Also, if I change re target to this, I do not receive any errors:
re:
#$(MAKE) fclean
#$(MAKE) all
Could not find solutions to my problems anywhere on the Internet.
Touching libprintf.a won't cause anything to rebuild. Make rebuilds targets that are out of date. Out of date means that either the target doesn't exist, or some prerequisite of the target is newer than the target.
After you run make then libprintf.a is up to date, which means it's newer than all its prerequisites. Running touch libprintf.a just makes it even newer than its prerequisites than it was before, so make still considers it up to date.
If you want to rebuild libprintf.a you need to touch (or delete) one of its prerequisites, not the target itself.
The reason for your second issue seems to be related to the makefile in the libft subdirectory. It seems like you're using the --no-print-directories option here: you should avoid using that, at least as long as you're debugging, so that you can see where make is going and which makefile it's running.

Make not building dot files before pdf

I have the current folder structure for my project
.
├── Makefile
└── S6
├── CD_CS304.md
├── CN_CS306.md
├── DAA_CS302.md
└── graphviz
└── cs304_compilerphases.dot
2 directories, 5 files
I am building separate pdfs for each and every markdown file, here is my Makefile
# Generate PDFs from the Markdown source files
#
# In order to use this makefile, you need some tools:
# - GNU make
# - Pandoc
# All markdown files are considered sources
MD_SOURCES := $(wildcard **/*.md)
OUTPUT_PDFS := $(MD_SOURCES:.md=.pdf)
DOT_SOURCES := $(wildcard **/*.dot)
OUTPUT_DOTPNGS := $(DOT_SOURCES:.dot=.png)
all: $(OUTPUT_DOTPNGS) $(OUTPUT_PDFS)
# Recipe for building png files from dot files
%.png: %.dot
dot \
-Tpng $< \
-o $#
# Recipe for converting a Markdown file into PDF using Pandoc
%.pdf: %.md
pandoc \
--variable fontsize=12pt \
--variable date:"\today" \
--variable geometry:a4paper \
--variable documentclass:book \
--table-of-contents \
--number-sections \
--filter pandoc-fignos \
-f markdown $< \
-o $#
.PHONY : clean
clean: $(OUTPUT_PDFS) $(OUTPUT_DOTPNGS)
$(RM) $^
I want to embed the output of the dot program into the latex pdf's but here the Makefile does not make the dot files into png and goes straight into compiling the pdf.
This makes the pdf compilation run into errors as the png files are not present.
If you want to ensure that one file is built before another file, add a dependency.
Change this:
%.pdf: %.md
to this:
%.pdf: %.md $(OUTPUT_DOTPNGS)
This dependency says, "Don't build this pdf file unless you've built every png file."

How to optimize Makefile

I write a ugly copy/paste way created Makefile:
all: download install
install: \
${EXTERNAL_MODELS_LOCAL}/squeezenet_weights_tf_dim_ordering_tf_kernels.h5 \
${EXTERNAL_MODELS_LOCAL}/resnet50_weights_tf_dim_ordering_tf_kernels.h5 \
${EXTERNAL_MODELS_LOCAL}/inception_v3_weights_tf_dim_ordering_tf_kernels.h5 \
${EXTERNAL_MODELS_LOCAL}/squeezenet_weights_tf_dim_ordering_tf_kernels.h5:
ln -s ${EXTERNAL_MODELS_ROOT}/squeezenet_weights_tf_dim_ordering_tf_kernels.h5 $#
${EXTERNAL_MODELS_LOCAL}/resnet50_weights_tf_dim_ordering_tf_kernels.h5:
ln -s ${EXTERNAL_MODELS_ROOT}/resnet50_weights_tf_dim_ordering_tf_kernels.h5 $#
${EXTERNAL_MODELS_LOCAL}/inception_v3_weights_tf_dim_ordering_tf_kernels.h5:
ln -s ${EXTERNAL_MODELS_ROOT}/inception_v3_weights_tf_dim_ordering_tf_kernels.h5 $#
download: $(EXTERNAL_MODELS_ROOT)/ \
$(EXTERNAL_MODELS_ROOT)/squeezenet_weights_tf_dim_ordering_tf_kernels.h5 \
$(EXTERNAL_MODELS_ROOT)/resnet50_weights_tf_dim_ordering_tf_kernels.h5 \
$(EXTERNAL_MODELS_ROOT)/inception_v3_weights_tf_dim_ordering_tf_kernels.h5 \
$(EXTERNAL_MODELS_ROOT)/squeezenet_weights_tf_dim_ordering_tf_kernels.h5:
wget https://github.com/OlafenwaMoses/ImageAI/releases/download/1.0/squeezenet_weights_tf_dim_ordering_tf_kernels.h5 \
-O $#
$(EXTERNAL_MODELS_ROOT)/resnet50_weights_tf_dim_ordering_tf_kernels.h5:
wget https://github.com/OlafenwaMoses/ImageAI/releases/download/1.0/resnet50_weights_tf_dim_ordering_tf_kernels.h5 \
-O $#
$(EXTERNAL_MODELS_ROOT)/inception_v3_weights_tf_dim_ordering_tf_kernels.h5:
wget https://github.com/OlafenwaMoses/ImageAI/releases/download/1.0/inception_v3_weights_tf_dim_ordering_tf_kernels.h5 \
-O $#
Biggest part skipped ,but looks the same. Is it possible to optimize this boilerplate?
A mixture of make variables, make automatic variables (e.g. $<, $#), make functions (e.g. addsuffix, addprefix) and pattern rules, maybe:
RHOST := https://github.com/OlafenwaMoses/ImageAI/releases/download/1.0/
H5STEM := squeezenet resnet50 inception_v3
H5 := $(addsuffix _weights_tf_dim_ordering_tf_kernels.h5,$(H5STEM))
H5LOCAL := $(addprefix $(EXTERNAL_MODELS_LOCAL)/,$(H5))
H5ROOT := $(addprefix $(EXTERNAL_MODELS_ROOT)/,$(H5))
.PHONY: install download
install: $(H5LOCAL)
download: $(H5ROOT)
$(EXTERNAL_MODELS_LOCAL)/%.h5: $(EXTERNAL_MODELS_ROOT)/%.h5
ln -s $< $#
$(EXTERNAL_MODELS_ROOT)/%.h5:
wget $(RHOST)/$*.h5 -O $#
And of course, if there was a way to automatically discover the list of remote *.h5 files, it would be even better. But some more information is needed to imagine how to do it (ssh, curl, wget... ?). The make shell function would be the starting point, of course:
H5 := $(shell <the-command-that-lists-the-remote-h5-files>)

GNU make: friendly way to choose mutually exclusive files

I'm writing a GNU makefile to create a license file like so: if the customer's file exists then copy it to the package directory, else use the generic license. I have about a dozen files that have the same copy pattern. The goal is to copy one of the two mutually exclusive source files onto the target filename.
Is there a better way to express this with GNU make syntax? Here's what I currently have. I considered copying all GENERIC files to the directory, then overwriting with the existing CUST files.
$(PDIR)/license.txt: | $(PDIR)
if [ -f Package/license.txt.$(CUST) ] ; \
then \
cat Package/license.txt.$(CUST) >$(PDIR)/license.txt ; \
else \
cat Package/license.txt.GENERIC >$(PDIR)/license.txt ; \
fi
Edit: Thanks to MadScientist for the help. Here's my final working version:
TARGETS = license.txt ...
final: $(addprefix ${PDIR}/,${TARGETS})
#echo some output
$(foreach T,${TARGETS},$(eval ${PDIR}/$T: $(firstword $(wildcard Package/$T.${CUST} Package/$T.GENERIC Package/$T)) | ${PDIR}))
$(addprefix ${PDIR}/,${TARGETS}):
#echo Creating substituted version of $< as $#
#sed --expression="\
... \
< '$<' \
> '$#'
${PDIR}:
mkdir $#
You can use wildcard, then (as always) you should use automatic variables. Like this:
$(PDIR)/license.txt: $(firstword $(wildcard Package/license.txt.$(CUST) Package/license.txt)) | $(PDIR)
cat '$<' > '$#'
If you have a lot of these you can also use a loop to define the targets:
TARGETS = license.txt
$(foreach T,$(TARGETS),$(eval $(PDIR)/$T: $(firstword $(wildcard Package/$T.$(CUST) Package.$T) | $(PDIR))))
$(addprefix $(PDIR)/,$(TARGETS)):
cat '$<' > '$#'

How to write a makefile?

I have just discovered that a LaTeX compilation could be launched from a makefile. I came to this from the need to generate a series of targets with some alterations of parameters.
I have the file at the bottom. How can I add other compilation jobs, with different optional parameters say. Said otherwise, how to change the name of the target file(s) when there is more than one?
MyFileNew.pdf : MyFile.tex
pdflatex "\def\UseOption{nonumber,nographics,e} \input{MyFile.tex}"
#makefile
MyFile.pdf : MyFile.tex
pdflatex "\def\UseOption{number,graphics,ef} \input{MyFile.tex}"\
pdflatex "\def\UseOption{number,graphics,ef} \input{MyFile.tex}"
#echo «Removing auxilliary LaTeX files resulting from compilation»
#rm -f *.log *.aux *.dvi *.toc *.lot *.lof
#end of makefile
Are you perhaps looking for something like this?
pdflatex := pdflatex "\def\UseOption{$(options_$*)} \input{$<}"
cleanup := rm -f *.log *.aux *.dvi *.toc *.lot *.lof
options_MyFileNew := nonumber,nographics,e
options_MyFile := number,graphics,ef
MyFileNew.pdf MyFile.pdf: %.pdf: MyFile.tex
$(pdflatex)
$(pdflatex)
$(cleanup)
This is basically just a refactoring of what you already have.
The following works finally as expected (except for cleaning the auxilliary files).
Makefile
.PHONY: all clean clean-all
all : TargetFile1.pdf TargetFile2.pdf
TargetFile1.pdf : MyFile.tex
pdflatex --jobname=$(#:.pdf=) "\def\UseOption{number,nographics,e} \input{MyFile.tex}"
TargetFile2.pdf : MyFile.tex
pdflatex --jobname=$(#:.pdf=) "\def\UseOption{number,nographics,ef} \input{MyFile.tex}"
clean :
rm -f *.log *.aux *.dvi *.toc *.lof
clean-all : clean
rm -fr *.pdf
# Makefile
Simple make file
all: main.o module.o \\ The dependencies of the target all \
gcc main.o module.o -o target_bin \\ The action to make the target all \
main.o: main.c module.h \\ The dependencies fo the target main.o \
gcc -I . -c main.c \\ The action to make the target main.o ,\
module.o: module.c module.h \
gcc -I . -c module.c \\ -I tells the compiler header file locations \
clean: \\ This target has no dependencies \
rm -rf *.o \
rm target_bin
Answering this question requires a relatively long explanation and a multi files for demonstration, I included a HERE a link to a github repo that answers this question in a compact (with enough details).

Resources