I’m really nerdy. So nerdy in fact, that every one of my writing projects, from active things to plot bunnies I ignore after a week, has a main file that looks roughly like this:
\input{document-preamble} \begin{document} \input{document-gls}
\ifsubmission{
\input{synopsis}
} {
\pagestyle{empty}
\input{halftitle}
\clearpage
\input{other-works}
\cleardoublepage
\input{title}
\input{copyright}
\clearpage
\input{dedication}
\clearpage
\input{epigraph}
\cleardoublepage
\input{halftitle}
\cleardoublepage
\pagestyle{fancy}
\pagenumbering{arabic}
\setcounter{page}{3}
}
\input{part-A}
\input{part-B}
\input{part-C}
\input{part-D}
\ifsubmission {
\printglossary[type=special]
} {
\input{colophon}
}
\end{document}
There are a few cool things going on here.
- Submission mode (which is formatted as a manuscript due to some other settings) includes a synopsis, but non-submissions don’t. Non-submission mode (which is typeset) includes the regular front-matter stuff like title, copyright, and epigraph pages.
- In submission mode only, a list of all special characters gets included at the end; non-submission builds instead have a colophon.
- The actual sections of a project are broken into parts (in this case, parts A, B, C, and D. It’s not shown here, but I can also build just those sections of a project if I want to focus on editing a specific section. Since I use part designators for major story events (not necessarily parts of a three-act structure), the number of these can vary.
- Stuff that’s common for all the documents I’ll generate (e.g., the part documents mentioned above and story bible) is kept in external files (document-preamble and document-gls).
All in all, I can effectively copy this document and use it for any project, only modifying the number of part-* files. At the end of the day, it’s trivial to give even the most flimsy plot bunny all the bells and whistles (and seeing a document typeset well acts as encouragement).
Because the above snippet is so similar across projects though, what if I could avoid copying it everywhere and have tooling generate it? Since the only parts that really need to change are the parts I’m inputting, surely I can knock out a half-assed script to build everything.
Really, that file only has to exist to make LaTeX happy. In fact, lots of files only need to exist for the sake of building, and their actual contents are pretty irrelevant. The title and half title pages for example, will be the title and my name. The copyright page has a ton of information, but most of it is (again) copy and pasted. Other works (when I actually have them mind you) could easily just be a list that’s static across every project.
The only parts that are actually unique in every project:
- the title (in the preamble file)
- the glossaries (document-gls)
- the synopsis
- the epigraph and dedication pages
- the main matter (part-{A,B,C,D})
Because I’m using CMake to build everything, I decided to just have CMake generate the common stuff for me; now it’s not even cluttering up my git history. In fact, the creation function is pretty simple:
include(create_target)
set(CREATE_FULL_DIR "${CMAKE_CURRENT_LIST_DIR}")
function(create_full codename parts number tex_files)
foreach(part IN LISTS parts)
set(BOOK_PARTS "${BOOK_PARTS}\n\input{part-${part}}")
endforeach()set(BOOK_CODENAME ${codename}) set(BOOK_NUMBER ${number}) configure_file("${CREATE_FULL_DIR}/halftitle.tex" "${CMAKE_CURRENT_BINARY_DIR}/halftitle.tex" COPYONLY ) configure_file("${CREATE_FULL_DIR}/full.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}-full.tex" @ONLY ) configure_file("${CREATE_FULL_DIR}/title.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/title.tex" @ONLY ) # These will create targets for building configure_file("${CREATE_FULL_DIR}/submission.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}.tex" @ONLY ) create_target(${codename} "${tex_files}" TRUE) configure_file("${CREATE_FULL_DIR}/pub.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}-pub.tex" @ONLY ) create_target("${codename}-pub" "${tex_files}" FALSE) # alternate layouts foreach(layout IN ITEMS "lulu") set(LAYOUT_STYLE ${layout}) configure_file("${CREATE_FULL_DIR}/pub-alt.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}-${layout}.tex"
@ONLY ) create_target("${codename}-${layout}" "${tex_files}" FALSE) endforeach()
endfunction()
Now all my projects need to do is add a few lines to their CMakeLists.txt and I get all this beautifulness happening automatically.
include(create_full)
# ...
create_full("wolf" "A;B;C;D" "I" "${tex_files}")
So why is all this worth the trouble though? Well, for one humans are really good at working with patterns; it’s why somethings you just know that some moron on the road is going to cut you off, or that your dog got into something she wasn’t supposed to, because you picked up on tiny details you weren’t actively observing but have learned to recognize and react to. If you want a practical example, go play any of the Mega Man games and see how long it takes before you can beat one of the bosses without any chance of dying (Chill Penguin in Mega Man X is a good place to start).
Now I’ve managed to pattern the basics of every writing project I have; all the little things that should be identical across products (fonts, formatting, the order of front and back matter) are the same. The stuff that’s actually in a project repository on the other hand is actually unique to the project with only minimal boilerplate (basically enough to leverage the CMake helper stuff).
But does any of this really matter? Other than people who want to nerd out on automation and generating files, is their a practical purpose to using patterns like this?
Of course.
I’ve been sitting on some sanity-checking scripts for a while, mostly to help find filter words and stuff like that. I’ve never bothered to automate them precisely because when I wrote the scripts, each of my projects was a special snowflake that’d require unique work and I’m way too lazy for that. Now that my projects look the same…it’s borderline trivial to create a small suite of scripts to run the stuff I’ve already written against anything with the expected project structure.
Not only that, but I’m a fan of automating basic tasks; now I can add more Jenkins jobs since I know what to expect. In fact, the jobs I use already depend on having some basic things in common. Using tools to fill in the common parts for my projects is simply a more efficient method than copying and tweaking files on a per-project basis.