-
-
Save clamytoe/68d13bb8481fc7acb81e373dea921d7d to your computer and use it in GitHub Desktop.
Generate HTML, PDF, EPUB and MOBI from ASCIIDoctor Source
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Makefile - Generate HTML, PDF, EPUB, MOBI from ASC | |
# This is how you assign a string to a variable name in 'make'. The | |
# variable name doesn't have to be all caps and the equal doesn't have | |
# to be snugged up to the variable name; it's just how I write | |
# Makefiles | |
FILENAME= the_document | |
# $(IDENTIFIER) is how you reference a 'make' variable, you can use | |
# ${} too, but I prefer $(). If you forget the parantheses or the | |
# curly braces, eg $INDENTIFER, 'make' will interpret that as $I with | |
# NDENTFIER appended to it. Probably not what you are expecting. | |
SOURCE= $(FILENAME).asc | |
HTML= $(FILENAME).html | |
PDF= $(FILENAME).pdf | |
EPUB= $(FILENAME).epub | |
MOBI= $(FILENAME).mobi | |
# $(shell shell command ) is how you invoke a command and save the | |
# results to a variable. It gets kinda tricky since every time you | |
# reference $(DATE) it will execute the command. The weird assignment | |
# operator := means just assign it one time. | |
DATE := $(shell date +%Y-%m-%d) | |
# Again, calling shell. This time using 'awk' to pull out the revision | |
# number. | |
# | |
# The '$' needs to be doubled in the command string to keep 'make' from | |
# trying to expand '$2' into something we didn't intend. | |
# I know there was a grep|cut in the bash version, I prefer to use 'awk' | |
# for these kinds of 'snip' operations since it's a single process | |
# invocation. Those are easier to deal with in this context since you | |
# don't have to worry about any pipe weirdness imposed by 'make'. | |
REVISION := $(shell awk '/revnumber/ {print $$2}' $(SOURCE)) | |
# Ok this is a "function" definition that we use to build the various | |
# ASCIIDOCTOR invocations. We could have just written the format | |
# specific definitions: | |
# | |
# ADOC_HTML= bundle exec asciidoctor | |
# ADOC_PDF= bundle exec asiidoctor-pdf | |
# ... | |
# | |
# The advantage of this technique is you only have to change | |
# the BUNDLE_EXEC part if the way you invoke asciidoctor | |
# changes (I don't know why it would change, but the idea | |
# is to isolate stuff that's repeated so you don't have to | |
# change it everywhere). | |
# Macro or 'function' definition | |
BUNDLE_EXEC= bundle exec $(1) | |
# Using the macro | |
ASCIIDOCTOR_HTML= $(call BUNDLE_EXEC,asciidoctor) | |
ASCIIDOCTOR_PDF= $(call BUNDLE_EXEC,asciidoctor-pdf) | |
ASCIIDOCTOR_EPUB= $(call BUNDLE_EXEC,asciidoctor-epub3) | |
ASCIIDOCTOR_MOBI= $(call BUNDLE_EXEC,asciidoctor-epub3) | |
# Here we build the shared flags used by asciidoctor by all | |
# invocations. I use the += assignment to show how you can add to a | |
# variable after it's initial assignment. | |
ADOC_FLAGS= --attribute revnumber=$(REVISION) | |
ADOC_FLAGS+= --attribute revdate=$(DATE) | |
# This next bit is some 'make' magic. The .PHONY rule is how we tell | |
# 'make' that some of our rules aren't associated directly with a | |
# file. The 'all' rule below is by default a dependency of .PHONY | |
# | |
# We define four rules html, pdf, epub and mobi later on. | |
.PHONY: html pdf epub mobi | |
# The default rule that 'make' looks for when invoked without arguments | |
# is 'all'. To build a rule (also sometimes called a target), 'make' | |
# builds the dependencies listed after the rulename: | |
# | |
# rulename: dep1 dep2 dep3 ... depN | |
# command_0 | |
# @command_1 # echoing the command is suppressed | |
# -command_2 # a command whose exit code is ignored | |
# -@command_3 # | |
# ... | |
# command_n | |
# | |
# dep1: subdep1 ... | |
# | |
# Here, the dependencies for 'all' are $(HTML), $(PDF) and $(EPUB) | |
# which expand to the names of the files that asciidoctor will | |
# create. So 'make all' will run the $(HTML), $(PDF) and $(EPUB) rules | |
# in that order. | |
all: $(HTML) $(PDF) $(EPUB) | |
# The $(HTML) rule depends on $(SOURCE), and only executes if the | |
# source file has changed or the destination file does not exist. $@ | |
# is an alias for the name of the rule to be used in the body of the | |
# recipe. | |
# | |
# By default, make will print the command that is being executed to | |
# stdout followed by it's output. To suppress printing the command, | |
# preface the command with an @. | |
# | |
# Lastly, the indention is a TAB and not 8 spaces. Make is an | |
# old-school tool and will complain if it doesn't get tabs. | |
$(HTML): $(SOURCE) | |
@echo Converting $(SOURCE) to $@ | |
@$(ASCIIDOCTOR_HTML) $(ADOC_FLAGS) $(SOURCE) | |
# The html target is one of those .PHONY targets mentioned earlier. | |
# It creates a file, just indirectly. Things get funky when phony | |
# targets are dependencies of other rules. I like to group related | |
# targets together. | |
html: $(HTML) | |
# empty recipes are ok | |
# The $(PDF) target is very similar to $(HTML) execpt the command | |
# used to generate the file is $(ASCIIDOCTOR_PDF). It's followed | |
# by the bare pdf target with $(PDF) as it's dependency and an | |
# empty recipe body. | |
$(PDF): $(SOURCE) | |
@echo Converting $(SOURCE) to $@ | |
@$(ASCIIDOCTOR_PDF) $(ADOC_FLAGS) $(SOURCE) | |
pdf: $(PDF) | |
# This is starting to be old hat, $(EPUB) mutates the command | |
# and the rest of the recipe looks the same as $(PDF) and $(HTML). | |
$(EPUB): $(SOURCE) | |
@echo Converting $(SOURCE) to $@ | |
@$(ASCIIDOCTOR_EPUB) $(ADOC_FLAGS) $(SOURCE) | |
epub: $(EPUB) | |
# Now this is different. The Mobi format is generated by | |
# $(ASCIIDOCTOR_EPUB), selected by additional arguments to the | |
# command. | |
# | |
# This could be handled two ways, adding the new arguments to the | |
# definition of $(ASCIIDOCTOR_MOBI) like this: | |
# | |
# | |
# ASCIIDOCTOR_MOBI= $(call BUNDLE_EXEC,asciidoctor-epub3 -a ebook-format=kf8) | |
# | |
# This is pretty clean since it groups all the options and commands | |
# together in one place in the Makefile and to be honest, this is the | |
# better solution in retrospect. | |
# | |
# Another way to accomplish this is mutate $(ADOC_FLAGS) for just this | |
# target, $(MOBI). Defining a rule multiple times is additive for the | |
# rule, so the first $(MOBI) updates $(ADOC_FLAGS) the second $(MOBI) | |
# rule checks to see if the $(MOBI) file exists, checks the state of | |
# it's dependencies and executes the recipe accordingly with the | |
# updated flags. | |
# | |
# This solution is helpful when you need to mutate flags for specific | |
# targets and keeps the mutations close to the target definitions. | |
$(MOBI): ADOC_FLAGS += -a ebook-format=kf8 | |
$(MOBI): $(SOURCE) | |
@echo Converting $(SOURCE) to $@ | |
@$(ASCIIDOCTOR_MOBI) $(ADOC_FLAGS) $(SOURCE) | |
mobi: $(MOBI) | |
# The debug rule is how I checked to make sure all of the | |
# variables I constructed contained the things I thought they | |
# should. | |
debug: | |
@echo 'Rule -> $@' | |
@echo ' SOURCE: $(SOURCE)' | |
@echo ' REVISION: $(REVISION)' | |
@echo ' HTML: $(HTML)' | |
@echo ' PDF: $(PDF)' | |
@echo ' EPUB: $(EPUB)' | |
@echo ' MOBI: $(MOBI)' | |
@echo ' ADOC_FLAGS: $(ADOC_FLAGS)' | |
@echo 'ASCIIDOCTOR HTML: $(ASCIIDOCTOR_HTML)' | |
@echo ' ASCIIDOCTOR PDF: $(ASCIIDOCTOR_PDF)' | |
@echo 'ASCIIDOCTOR MOBI: $(ASCIIDOCTOR_MOBI)' | |
@echo 'ASCIIDOCTOR EPUB: $(ASCIIDOCTOR_EPUB)' | |
# Often times we want to restart from a known good "clean" state. | |
# A clean rule is a good place to remove transient files so you ensure | |
# that all your dependencies in your project are rebuilt. In this | |
# case we just remove the translated files. We could use wildcards | |
# in this rule like 'rm *.html' but this can have unintended consequences | |
# if we have other files in HTML format that we didn't want to smoke. | |
# | |
# Always be as explicit as possible in clean rules. | |
clean: | |
@/bin/rm -f $(HTML) $(PDF) $(EPUB) $(MOBI) | |
# Usage | |
# | |
# I'll outline some of the ways this makefile can be used: | |
# | |
# ## Generate $(HTML), $(PDF), $(PUB) Files | |
# | |
# $ make | |
# | |
# If all those files exist and the source file hasn't changed | |
# make will exit without doing anything. Win! | |
# | |
# | |
# ## Generate Just $(PDF) | |
# | |
# $ make the_document.pdf | |
# | |
# | |
# ## Specify a Different FILENAME with the 'all' Target | |
# | |
# $ make FILENAME=other_doc | |
# | |
# This will create files called other_doc.html, other_doc.pdf, and | |
# other_doc.epub from the source file other_doc.asc. | |
# | |
# ## Generate Files with Shortcut Rule Names | |
# | |
# $ make pdf | |
# | |
# The pdf rule is dependent on the rule $(PDF) so that rule | |
# is executed if necessary before the empty body of the pdf | |
# rule is executed. | |
# | |
# ## See What Commands 'make' Would Execute for a Target | |
# | |
# $ make -n | |
# $ make -n pdf | |
# $ make -n the_document.pdf | |
# $ make -n mobi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment