0
votes

I'm trying to build some PDFs in a Makefile using Sphinx. The resulting PDF has broken references, so I want to fix those using pdftk.

Goal

So what I want to do for all PDFs I build is this:

# Creates the PDF files.
$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken

# Go through all PDFs and fix them.
pdftk $(BUILDDIR)/pdf_broken/thepdf.pdf output $(BUILDDIR)/pdf/thepdf.pdf

Attempt with Make

So to do this with Make I have written this Makefile:

# Build PDF (results in broken references)
$(BUILDDIR)/pdf_broken/%.pdf:
    $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken

# This fixes the broken pdfs and produces the final result.
$(BUILDDIR)/pdf/%.pdf: $(BUILDDIR)/pdf_broken/%.pdf
    mkdir -p $(BUILDDIR)/pdf/
    pdftk $^ output $@

pdf: $(BUILDDIR)/pdf/%.pdf

Expected result

I'm using Pattern matching as I understand it from reading the manual: http://www.tack.ch/gnu/make-3.82/make_91.html

Where $< as I understand it should be the prerequisite expanded so from my above example:

$(BUILDDIR)/pdf_broken/thepdf.pdf

and then $@ should be the target:

$(BUILDDIR)/pdf/thepdf.pdf

So my recipe pdftk $^ output $@ should run the command:

pdftk $(BUILDDIR)/pdf_broken/thepdf.pdf output $(BUILDDIR)/pdf/thepdf.pdf

Actual result

But this is not what is happening. Instead, this is run:

pdftk build/pdf_broken/%.pdf output build/pdf/%.pdf

Which obviously gives me an error:

Error: Unable to find file.
Error: Failed to open PDF file:
   build/pdf_broken/%.pdf

Question

So my question is, what have I missundestood with how the pattern matching works, and how is the correct way to solve this using Make?

1
You're confusing patterns with wildcards in your rule for pdf, but aside from that how is make supposed to know which files will ultimately be created considering it has no idea about the output of sphinx?user657267

1 Answers

1
votes

You should likely lookup pattern rules. In any case, it looks like you have a single command to generate all the files in the broken directory. This should have its own rule, and should likely output a dummy file to indicate it is complete. Your rule to fix the pdf files should be dependent on this dummy target being created.

It should be something like:

// get a list of expected output files:
PDF_SOURCES:=$(wildcard source/pdf/*)
PDF_OUTS:=$(patsubst $(PDF_SOURCES),source/pdf/%.pdf,$(BUILDDIR)/pdf/%.pdf);

// just for debugging:
$(info PDF_SOURCES = $(PDF_SOURCES))
$(info PDF_OUTS = $(PDF_OUTS))


// default rule
all: $(PDF_OUTS)
    @echo done

// rule to build BUILDIR:
$(BUILDDIR)/pdf:
     mkdir -p $@

// rule to build all broken files in one go:
// (note: generates a file .dosphynx, which is used to keep track
// of when the rule was run last.   This rule will be run if the 
// timestamp of any of the sources are newer. 
.do_sphynx: $(PDF_SOURCES) | $(BUILDDIR)/pdf
     $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken
     touch $@

// create a dependency of all output files on do_sphynx
$(PDF_OUTS): .do_sphynx

// patern rule to fix pdf files
$(BUILDDIR)/pdf/%.pdf : $(BUILDDIR)/pdf_broken/%.pdf
    pdftk $< output $@

I've not tested this, so its possible it may have a syntax error in it..

---------------------- EDIT -------------

Ok, since $(PDF_OUTS) cannot be determined at makefile read time, perhaps you should do:

// get a list of expected output files:
PDF_SOURCES:=$(wildcard source/pdf/*)

all: .do_fix
    @echo done

$(BUILDDIR)/pdf:
     mkdir -p $@

.do_sphynx: $(PDF_SOURCES) | $(BUILDDIR)/pdf
    $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) source/pdf/ $(BUILDDIR)/pdf_broken
    touch $@

.do_fix: .do_sphynx
    @for src in $$(ls source/pdf/*.pdf); do \
         trg=$${src/#"source/pdf"/"$(BUILD_DIR)/pdf"}; \
         [[ $$src -nt $$trg ]] && \
            echo "$$src ==> $$trg" && pdftk $$src output $$trg; \
    done
    touch $@

One note -- the -nt comparator in the if will return true if $trg does not exist, so it will cover the case where the file is missing, or the target is older than the source. Again not tested, but it should work.