Make not building dot files before pdf - makefile

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."

Related

GNU Make how to make a static pattern rule for files that are not in the same directory?

I want to use make and create a static pattern rule that has the target in a output directory, and the prerequisite files are in the preceeding directory, and it has to work recursively.
I have a minimal example here:
.
├── anotherdir
│   ├── output
│   │   ├── source3.md
│   │   └── source4.md
│   ├── source3.json
│   └── source4.json
├── output
│   ├── source1.md
│   └── source2.md
├── source1.json
└── source2.json
I want to generate the output directories if they do not exist, and I want to generate *.md files from the *.json using make if they do not exist, or *.json is updated.
So far, I have the following Makefile:
SOURCE_FILES := $(shell find ./ -name "*.json")
OUTPUT_FILES := $(join $(addsuffix output/,$(dir $(SOURCE_FILES))), $(addsuffix .md,$(basename $(notdir $(SOURCE_FILES)))))
.PHONY: all
all: $(OUTPUT_FILES)
$(OUTPUT_FILES): %.md: %.json
mkdir -p $(dir $#)
# Command to create MD file from json file into the output directory here
The actual command to create the MD file from the json file doesn't matter here, because I have a script that I will call that will do this for me. The problem here, is that when I try to even run this at all, I get the following output:
> make all
make: *** No rule to make target 'anotherdir/output/source4.json', needed by 'anotherdir/output/source4.md'. Stop.
Obviously, source4.json is not in anotherdir/output, but rather, it's in the preceeding directory, which is just anotherdir. I don't know how to make it so that the pattern $(OUTPUT_FILES): %.md: %.json will match it properly.
Or is a static pattern rule not good here? I'm not sure what to do to fit my scenario.
EDIT: I tried to do something like this:
$(OUTPUT_FILES): %.md: $(join $(subst output,,$(dir %)), $(addsuffix .json,$(basename $(notdir %))))
and this doesn't work, I still get:
> make all
make: *** No rule to make target 'anotherdir/output/source4.json', needed by 'anotherdir/output/source4.md'. Stop.
Edit 2: to clarify, i start with the following files
.
├── anotherdir
│ ├── source3.json
│ └── source4.json
├── source1.json
└── source2.json
And then when i run make, i want it to generate the output folders like this
.
├── anotherdir
│ ├── output
│ │ ├── source3.md
│ │ └── source4.md
│ ├── source3.json
│ └── source4.json
├── output
│ ├── source1.md
│ └── source2.md
├── source1.json
└── source2.json
I want to use some kind of smart makefile syntax to pick up these files names without me hard coding it in myself. Hence, i looked at the documentation and saw that static pattern rules might be the solution that i want, except that i can't get the right prerequisite pattern down.
I would do it this way:
First, find the source files just as you did (with a small change to prevent the unsightly double-slash):
SOURCE_FILES := $(shell find . -name "*.json")
A pattern file would be nice, if we could use two wildcards at once, but Make can't quite do that. So I recommend using a template:
define template
TDIR := $(dir $(1))output
TARG := $$(TDIR)/$(notdir $(basename $(1))).md
$$(TARG): $(1)
mkdir -p $$#
#echo building $$# from $$<
# Command to create MD file from json file into the output directory here
endef
$(foreach SOURCE,$(SOURCE_FILES),$(eval $(call template,$(SOURCE))))
If this works, all that's left is to construct a list of output files, and a default rule that has all of them as prerequisites:
define template
TDIR := $(dir $(1))output
TARG := $$(TDIR)/$(notdir $(basename $(1))).md
OUTPUT_FILES += $$(TARG)
$$(TARG): $(1)
mkdir -p $$#
#echo building $$# from $$<
# Command to create MD file from json file into the output directory here
endef
all:
$(foreach SOURCE,$(SOURCE_FILES),$(eval $(call template,$(SOURCE))))
all: $(OUTPUT_FILES)
It isn't pretty, but it seems to work.
If it had not been proposed already in another answer I would have suggested foreach-eval-call. For completeness here are different solutions for GNU make (they may work also with other versions of make but I did not check):
Creating the output directories beforehand
If the output directories exist already you can refer to ../%.json in your pattern rule:
SOURCE_FILES := $(shell find . -name "*.json")
OUTPUT_FILES := $(join $(dir $(SOURCE_FILES)),\
$(patsubst %.json,output/%.md,$(notdir $(SOURCE_FILES))))
$(shell mkdir -p $(dir $(OUTPUT_FILES)))
.PHONY: all
all: $(OUTPUT_FILES)
%.md: ../%.json
: json2md $< -o $#
This may look strange but if you read carefully the Pattern match section of the GNU make manual you should quickly understand. The only constraint for this to work is that the output directories exist before make searches pattern rules that match the targets. If one does not exist make will complain that there is no eligible rule to build the target. This is the reason for the:
$(shell mkdir -p $(dir $(OUTPUT_FILES)))
at the beginning of the Makefile. Demonstration:
$ make
: json2md output/../source2.json -o output/source2.md
: json2md output/../source1.json -o output/source1.md
: json2md anotherdir/output/../source4.json -o anotherdir/output/source4.md
: json2md anotherdir/output/../source3.json -o anotherdir/output/source3.md
Using the secondary expansion
Secondary expansion gives you the possibility to use automatic variables in the list of prerequisites. The $$ are needed to escape the first expansion by make.
SOURCE_FILES := $(shell find . -name "*.json")
OUTPUT_FILES := $(join $(dir $(SOURCE_FILES)),\
$(patsubst %.json,output/%.md,$(notdir $(SOURCE_FILES))))
.PHONY: all
all: $(OUTPUT_FILES)
$(sort $(dir $(OUTPUT_FILES))):
mkdir -p $#
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(patsubst %output,%,$$(#D))$$(basename $$(#F)).json | $$(dir $$#)
: json2md $< -o $#
Demonstration:
$ make
mkdir -p output/
mkdir -p anotherdir/output/
: json2md source2.json -o output/source2.md
: json2md source1.json -o output/source1.md
: json2md anotherdir/source4.json -o anotherdir/output/source4.md
: json2md anotherdir/source3.json -o anotherdir/output/source3.md
Note: instead of creating the output directories in the json-to-md rule (which has the drawback of creating them several times), I added them as order-only prerequisites and added a specific rule to create them.
Note: the sort function also removes duplicates.
Using recursive make
Here we invoke make (with always the same Makefile) recursively in each sub-directory (except output, of course). Each invocation handles only the local json files, which makes the paths of prerequisites and targets much simpler.
MF := $(realpath $(lastword $(MAKEFILE_LIST)))
SUB_DIRS := $(filter-out . ./output,$(shell find . -maxdepth 1 -type d))
SOURCE_FILES := $(filter-out $(SUB_DIRS),$(wildcard *.json))
OUTPUT_FILES := $(patsubst %.json,output/%.md,$(SOURCE_FILES))
.PHONY: $(SUB_DIRS) all
all: $(SUB_DIRS) $(OUTPUT_FILES)
$(OUTPUT_FILES): output/%.md: %.json | output
: json2md $< -o $#
output:
mkdir -p $#
$(SUB_DIRS):
$(MAKE) -C $# -f $(MF)
Demonstration:
$ make
make -C anotherdir -f /home/doe/json2md/Makefile
make[1]: Entering directory '/home/doe/json2md/anotherdir'
mkdir -p output
: json2md source4.json -o output/source4.md
: json2md source3.json -o output/source3.md
make[1]: Leaving directory '/home/doe/json2md/anotherdir'
mkdir -p output
: json2md source2.json -o output/source2.md
: json2md source1.json -o output/source1.md

Collect files from another folder

I developed a script to convert all the markdowns files in my folder to ePub and place them in epubs.
Nevertheless I would like to place my files in the markdowns sub-folder and not in the main directory (the one where the makefile is).
.
├── epubs
│   └── blabla.epubs
├── makefile
└── markdowns
└── blabla.pd
How should I modify my code?
MARKDOWN=$(shell find . -iname "*.pd")
EPUBS=$(MARKDOWN:.pd=.epub)
.PHONY = all clean
all: $(EPUBS)
%.epub: %.pd
pandoc --from markdown --to epub --smart $< -o epubs/$#
clean:
rm epubs/*
One approach is to explicitly tell make in your pattern rule where the .pd prerequisites for the .epub targets reside:
MARKDOWN_DIR := markdowns
EPUB_DIR := epubs
MARKDOWNS := $(wildcard $(MARKDOWN_DIR)/*.pd)
EPUBS := $(subst $(MARKDOWN_DIR),$(EPUB_DIR),$(MARKDOWNS:.pd=.epub))
$(EPUB_DIR)/%.epub: $(MARKDOWN_DIR)/%.pd
pandoc --from markdown --to epub --smart $< -o $#
.PHONY: all clean
all: $(EPUBS)
Note two minor additional changes. I replaced your shell find command with make's $(wildcard) function because that is what it's for. Your pandoc command no longer needs epubs/ in front of $# because that directory is now part of the target. And I reordered a bit according to my liking.

Makefile recognizes only changed prerequisites, not new ones

Trying to write a simple makefile to compile Markdown files to HTML with Pandoc. I don't want to have to add all the prerequisite .md files explicitly to the makefile, so I've tried to use a pattern rule along with a wildcard prerequisite:
all: www/*.html
www/%.html: src/%.md
pandoc -f markdown -t html $< > $#
This is close, but only processes prerequisite .md files for which an .html target file already exists and is out of date. New .md files which don't already have an .html file get ignored (so if all the non-new files are built, I get make: Nothing to be done for 'all'.)
What's the concept I'm missing? I'm not sure how to tell make to run on both the changed AND new .md files in src/ and apply the pattern rule to each.
You could obtain a list of the .html files to be generated from the already existing .md files you have in the src/ directory.
First, the list of the existing .md files can be obtained with the wildcard built-in function:
md-files := $(wildcard src/*.md)
Then, apply a substitution reference to the md-files variable, so that the src/ prefix is removed and the suffix .md is replaced by .html for each element of the list of the .md files:
$(md-files:src/%.md=%.html)
Finally, by applying the addprefix built-in function to the resulting list, the prefix www/ can be added to each element of that list:
html-files := $(addprefix www/,$(md-files:src/%.md=%.html))
Putting everything together, the resulting makefile would be:
md-files := $(wildcard src/*.md)
html-files := $(addprefix www/,$(md-files:src/%.md=%.html))
.PHONY: all
all: $(html-files)
www/%.html: src/%.md
pandoc -f markdown -t html $< > $#
Try this:
$ cat Makefile
input := foo.md bar.md baz.md
output := $(input:%.md=www/%.html)
.PHONY: all
all: ${output}
www/%.html: src/%.md
pandoc -f markdown -t html $< > $#
$ make -n
pandoc -f markdown -t html src/foo.md > www/foo.html
pandoc -f markdown -t html src/bar.md > www/bar.html
pandoc -f markdown -t html src/baz.md > www/baz.html

Makefile for compiling Markdown files to a single PDF

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

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