2

For clarity, I am running this on windows with GnuWin32 make.

I have a set of directories with markdown files in at several different levels - theoretically they could be in the branch nodes, but I think currently they are only in the leaf nodes. I have a set of pandoc/LaTeX commands to run to turn the markdown files into PDFs - and obviously only want to recreate the PDFs if the markdown file has been updated, so a makefile seems appropriate.

What I would like is a single makefile in the root, which iterates over any and all sub-directories (to any depth) and applies the make rule I'll specify for running pandoc.

From what I've been able to find, recursive makefiles require you to have a makefile in each sub-directory (which seems like an administrative overhead that I would like to avoid) and/or require you to list out all the sub-directories at the start of the makefile (again, would prefer to avoid this).

Theoretical folder structure:

root
|-make
|-Folder AB
|   |-File1.md
|   \-File2.md
|-Folder C
| \-File3.md
\-Folder D
  |-Folder E
  | \-File4.md
  |-Folder F
    \-File5.md

How do I write a makefile to deal with this situation?

Mikhail Kholodkov
  • 23,642
  • 17
  • 61
  • 78
Vecna
  • 47
  • 5

2 Answers2

0

Here is a small set of Makefile rules that hopefuly would get you going

%.pdf : %.md
        pandoc -o $@ --pdf-engine=xelatex $^

PDF_FILES=FolderA/File1.pdf FolderA/File2.pdf \
   FolderC/File3.pdf FolderD/FolderE/File4.pdf FolderD/FolderF/File5.pdf

all: ${PDF_FILES}

Let me explain what is going on here. First we have a pattern rule that tells make how to convert a Markdown file to a PDF file. The --pdf-engine=xelatex option is here just for the purpose of illustration.

Then we need to tell Make which files to consider. We put the names together in a single variable PDF_FILES. This value for this variable can be build via a separate scripts that scans all subdirectories for .md files.

Note that one has to be extra careful if filenames or directory names contain spaces.

Then we ask Make to check if any of the PDF_FILES should be updated.

If you have other targets in your makefile, make sure that all is the first non-pattern target, or call make as make all

Updating the Makefile

If shell functions works for you and basic utilities such as sed and find are available, you could make your makefile dynamic with a single line.

%.pdf : %.md
        pandoc -o $@ --pdf-engine=xelatex $^

PDF_FILES:=$(shell find -name "*.md" | xargs echo | sed 's/\.md/\.pdf/g' )
all: ${PDF_FILES}

MadScientist suggested just that in the comments

Otherwise you could implement a script using the tools available on your operating system and add an additional target update: that would compute the list of files and replace the line starting with PDF_FILES with an updated list of files.

Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
  • Thanks for the starting point @dmitrichubarov, but I was specifically looking for a version where I didn't need to specify the sub-directories in the `makefile` by name so that the `makefile` remains constant as the project grows and changes. You mention that another script can build up the list of sub-directories - how would you suggest going about that part? – Vecna Jul 08 '18 at 13:15
  • 1
    If you were on a POSIX-based system such as GNU/Linux it would be trivial; use: `PDF_FILES := $(shell find . -name \*.pdf)`. Maybe someone who knows Windows can provide you a similar answer that works there. – MadScientist Jul 08 '18 at 14:05
  • @Vecna vonder if *MadScientist*'s suggestion would work for you. I have updated the post with some details. – Dima Chubarov Jul 08 '18 at 14:32
  • @DmitriChubarov - Thanks. I've made some progress but run up against the problem that make really doesn't seem to like spaces in the file path. Is there any easy way around that? Not sure if renaming all the folders and files in the project is feasible. – Vecna Jul 09 '18 at 16:35
  • @Vecna I am afraid there is no good solution to spaces in filenames within Makefiles (see https://stackoverflow.com/q/9838384/1328439 and answers there to get an idea of the complexity of the task). – Dima Chubarov Jul 10 '18 at 04:29
  • @DmitriChubarov Ahh yes, I see... Thank you - I shall have to ponder about where I take the project and then revisit! – Vecna Jul 10 '18 at 18:17
0

Final version of the code that worked for Windows, based on @DmitiChubarov and @MadScientist's suggestions is as follows:

%.pdf: %.md
    pandoc $^ -o $@

PDF_FILES:=$(shell dir /s /b *.md | sed "s/\.md/\.pdf/g")

all: ${PDF_FILES}
Vecna
  • 47
  • 5