find_asciidoc_bits.cmake revision 0bbfda8a
1#
2# Find and setup asciidoc[tor] bits
3#
4
5
6# First see if we can find the programs
7find_program(ASCIIDOCTOR asciidoctor)
8find_program(ASCIIDOC asciidoc)
9find_program(A2X a2x)
10find_program(DBLATEX dblatex)
11find_program(XMLTO xmlto)
12
13
14# If we have asciidoctor, we need to figure out the version, as manpage
15# output is relatively new.
16if(ASCIIDOCTOR)
17	execute_process(
18		COMMAND ${ASCIIDOCTOR} --version
19		RESULT_VARIABLE _adoctor_result
20		OUTPUT_VARIABLE _adoctor_verout
21		ERROR_QUIET
22	)
23	if(NOT ${_adoctor_result} EQUAL "0")
24		# Err...
25		message(WARNING "Unexpected result trying asciidoctor --version.")
26		set(_adoctor_verout "Asciidoctor 0.0.0 FAKE")
27	endif()
28	unset(_adoctor_result)
29
30	# Break out the version.
31	set(_adoctor_veregex "Asciidoctor ([0-9]+\\.[0-9]+\\.[0-9]+).*")
32	string(REGEX REPLACE ${_adoctor_veregex} "\\1"
33		ASCIIDOCTOR_VERSION ${_adoctor_verout})
34	unset(_adoctor_verout)
35	unset(_adoctor_veregex)
36	message(STATUS "Found asciidoctor (${ASCIIDOCTOR}) version ${ASCIIDOCTOR_VERSION}")
37
38	# 1.5.3 is the first release that can write manpages natively.  This
39	# means 1.5.3 dev versions after a certain point can as well; assume
40	# anybody running a 1.5.3 dev is keeping up well enough that it can
41	# DTRT too.  We assume any version can do HTML.
42	set(ASCIIDOCTOR_CAN_MAN  0)
43	set(ASCIIDOCTOR_CAN_HTML 1)
44	set(ASCIIDOCTOR_CAN_DBXML 1)
45	if(${ASCIIDOCTOR_VERSION} VERSION_GREATER "1.5.2")
46		set(ASCIIDOCTOR_CAN_MAN 1)
47	elseif(${ASCIIDOCTOR_VERSION} VERSION_LESS "0.0.1")
48		set(ASCIIDOCTOR_CAN_HTML 0)
49		set(ASCIIDOCTOR_CAN_DBXML 0)
50	endif()
51
52	# dblatex PDF output works fine with docbook5.  xmlto/docbook XSL
53	# manpage generation doesn't, so it has to override this.
54	set(ASCIIDOCTOR_DB_VER 5)
55endif(ASCIIDOCTOR)
56
57
58# For asciidoc, it doesn't really matter, but look up the version for
59# cosmetics anyway
60if(ASCIIDOC)
61	execute_process(
62		COMMAND ${ASCIIDOC} --version
63		RESULT_VARIABLE _adoc_result
64		OUTPUT_VARIABLE _adoc_verout
65		ERROR_QUIET
66	)
67	if(NOT ${_adoc_result} EQUAL "0")
68		# Err...
69		message(WARNING "Unexpected result trying asciidoc --version.")
70		set(_adoc_verout "asciidoc 0.0.0")
71	endif()
72	unset(_adoc_result)
73
74	# Break out the version.
75	set(_adoc_veregex "asciidoc ([0-9]+\\.[0-9]+\\.[0-9]+).*")
76	string(REGEX REPLACE ${_adoc_veregex} "\\1"
77		ASCIIDOC_VERSION ${_adoc_verout})
78	unset(_adoc_verout)
79	unset(_adoc_veregex)
80	message(STATUS "Found asciidoc (${ASCIIDOC}) version ${ASCIIDOC_VERSION}")
81
82	# Can always do both, unless horked
83	if(${ASCIIDOC_VERSION} VERSION_GREATER "0.0.0")
84		set(ASCIIDOC_CAN_MAN  1)
85		set(ASCIIDOC_CAN_HTML 1)
86		set(ASCIIDOC_CAN_DBXML 1)
87	endif()
88
89	# This is an example of 'horked'...
90	if(NOT A2X)
91		set(ASCIIDOC_CAN_MAN 0)
92	endif()
93
94	# Only docbook version python asciidoc supports
95	set(ASCIIDOC_DB_VER 45)
96endif(ASCIIDOC)
97
98
99# dblatex lets us build PDF's from the DocBook XML.  This is pretty
100# fringe and not part of normal builds, so try to minimize the impact of
101# the checks.
102if(DBLATEX)
103	# Don't really care about the version, so save the extra checks
104	if(0)
105		execute_process(
106			COMMAND ${DBLATEX} --version
107			RESULT_VARIABLE _dblatex_result
108			OUTPUT_VARIABLE _dblatex_verout
109			ERROR_QUIET
110		)
111		if(NOT ${_dblatex_result} EQUAL "0")
112			# Err...
113			message(WARNING "Unexpected result trying dblatex --version.")
114			set(_dblatex_verout "dblatex 0.0.0 FAKE")
115		endif()
116		unset(_dblatex_result)
117
118		# Break out the version.
119		set(_dblatex_veregex "dblatex version ([0-9]+\\.[0-9]+\\.[0-9]+).*")
120		string(REGEX REPLACE ${_dblatex_veregex} "\\1"
121			DBLATEX_VERSION ${_dblatex_verout})
122		unset(_dblatex_verout)
123		unset(_dblatex_veregex)
124		message(STATUS "Found dblatex (${DBLATEX}) version ${DBLATEX_VERSION}")
125	else()
126		message(STATUS "Found dblatex (${DBLATEX})")
127	endif()
128
129	# I guess it works...
130	set(DBLATEX_CAN_PDF 1)
131endif(DBLATEX)
132
133
134# xmlto is another frontend for DocBook XML -> stuff.  It can indirect
135# through dblatex (like we just do manually above) or through fop for PDF
136# output, but also knows how to invoke xsltproc to generate manpage
137# output, which gives us another route from adoc -> XML -> manpage.  And
138# potentially other formats, if we start caring.
139if(XMLTO)
140	# Don't really care about the version, so save the extra checks
141	if(0)
142		execute_process(
143			COMMAND ${XMLTO} --version
144			RESULT_VARIABLE _xmlto_result
145			OUTPUT_VARIABLE _xmlto_verout
146			ERROR_QUIET
147		)
148		if(NOT ${_xmlto_result} EQUAL "0")
149			# Err...
150			message(WARNING "Unexpected result trying xmlto --version.")
151			set(_xmlto_verout "xmlto 0.0.0 FAKE")
152		endif()
153		unset(_xmlto_result)
154
155		# Break out the version.
156		set(_xmlto_veregex "xmlto version ([0-9]+\\.[0-9]+\\.[0-9]+).*")
157		string(REGEX REPLACE ${_xmlto_veregex} "\\1"
158			XMLTO_VERSION ${_xmlto_verout})
159		unset(_xmlto_verout)
160		unset(_xmlto_veregex)
161		message(STATUS "Found xmlto (${XMLTO}) version ${XMLTO_VERSION}")
162	else()
163		message(STATUS "Found xmlto (${XMLTO})")
164	endif()
165
166	# I guess it can do whatever...
167	set(XMLTO_CAN_STUFF 1)
168endif(XMLTO)
169
170
171
172
173#
174# Generator functions for creating targets for the various
175# transformations.
176#
177
178# Lot of boilerplate in all of them
179macro(_ad_mk_boilerplate PROG OUT)
180	# Minimal seatbelt
181	set(my_usage "${PROG}_mk_${OUT}(<output> <input> [DEPENDS <deps>] [COMMENT <comment>])")
182	cmake_parse_arguments(
183		_ARGS
184		""
185		"COMMENT"
186		"DEPENDS"
187		${ARGN}
188	)
189	if(_ARGS_UNPARSED_ARGUMENTS)
190		message(FATAL_ERROR ${my_usage})
191	endif()
192
193	# Always depend on the input file, maybe on more
194	set(dependancies ${ADFILE})
195	if(_ARGS_DEPENDS)
196		list(APPEND dependancies ${_ARGS_DEPENDS})
197	endif()
198
199	# Come up with some comment or other
200	if(NOT _ARGS_COMMENT)
201		get_filename_component(basename ${OUTFILE} NAME)
202		set(_ARGS_COMMENT "Generating ${basename} with ${PROG}")
203	endif()
204endmacro(_ad_mk_boilerplate)
205
206
207# Build a manpage via asciidoctor
208function(asciidoctor_mk_manpage OUTFILE ADFILE)
209	# Guard
210	if(NOT ASCIIDOCTOR_CAN_MAN)
211		message(FATAL_ERROR "asciidoctor can't do man")
212	endif()
213
214	_ad_mk_boilerplate(asciidoctor manpage ${ARGN})
215
216	# Setup the rule
217	add_custom_command(OUTPUT ${OUTFILE}
218		DEPENDS ${dependancies}
219		COMMAND ${ASCIIDOCTOR} -b manpage -o ${OUTFILE} ${ADFILE}
220		COMMENT ${_ARGS_COMMENT}
221	)
222endfunction(asciidoctor_mk_manpage)
223
224
225# Build a manpage via asciidoc (technically, a2x)
226function(a2x_mk_manpage OUTFILE ADFILE)
227	# Guard
228	if(NOT A2X OR NOT ASCIIDOC_CAN_MAN)
229		message(FATAL_ERROR "asciidoc/a2x can't do man")
230	endif()
231
232	_ad_mk_boilerplate(a2x manpage ${ARGN})
233
234	# a2x gives us very little control over input/output files, so we
235	# have to do some vaguely stupid dances.  In theory, -D works for the
236	# manpage output, but it's doc'd not to and will warn, so don't even
237	# try.  The result is that it always puts the outfile file next to
238	# the input.  So we make a temporary dir (with a hopefully unique
239	# name) and do all our stuff in there.
240	get_filename_component(basedir ${ADFILE} DIRECTORY)
241	while(1)
242		string(RANDOM rndstr)
243		set(a2x_tmpdir "${basedir}/a2x.${rndstr}")
244		if(NOT IS_DIRECTORY ${a2x_tmpdir})
245			break()
246		endif()
247	endwhile()
248	file(MAKE_DIRECTORY ${a2x_tmpdir})
249
250	# This had better already be named "someprog.somesection.adoc",
251	# because a2x is going to magically figure the program and section
252	# name from the contents and make that output file.
253	get_filename_component(inbasename ${ADFILE} NAME)
254	string(REGEX REPLACE "(.*)\\.adoc$" "\\1" outbasename ${inbasename})
255	if(NOT outbasename)
256		message(FATAL_ERROR "Can't figure output for ${inbasename}")
257	endif()
258
259	# In/out tmpfile names
260	set(a2x_intmp  "${a2x_tmpdir}/${inbasename}")
261	set(a2x_outtmp "${a2x_tmpdir}/${outbasename}")
262
263	add_custom_command(OUTPUT ${OUTFILE}
264		DEPENDS ${dependancies}
265		COMMAND cp ${ADFILE} ${a2x_intmp}
266		COMMAND ${A2X} --doctype manpage --format manpage ${a2x_intmp}
267		COMMAND mv ${a2x_outtmp} ${OUTFILE}
268		COMMAND rm ${a2x_intmp}
269		COMMENT ${_ARGS_COMMENT}
270	)
271endfunction(a2x_mk_manpage)
272
273
274# Build a manpage via xmlto
275function(xmlto_mk_manpage OUTFILE XMLFILE)
276	# Guard
277	if(NOT XMLTO)
278		message(FATAL_ERROR "xmlto can't do man")
279	endif()
280
281	_ad_mk_boilerplate(xmlto manpage ${ARGN})
282
283	# As with a2x, this had better already be named
284	# "someprog.somesection.xml" because we have so little control over
285	# the output location.
286	get_filename_component(inbasename ${XMLFILE} NAME)
287	string(REGEX REPLACE "(.*)\\.xml$" "\\1" outbasename ${inbasename})
288	if(NOT outbasename)
289		message(FATAL_ERROR "Can't figure output for ${inbasename}")
290	endif()
291
292	get_filename_component(basedir ${XMLFILE} DIRECTORY)
293	add_custom_command(OUTPUT ${OUTFILE}
294		DEPENDS ${XMLFILE} ${dependancies}
295		COMMAND ${XMLTO}
296			--skip-validation
297			-o ${basedir}
298			# This apparently doesn't work right...
299			--stringparam 'man.endnotes.list.enabled=0'
300			man ${XMLFILE}
301		COMMENT ${_ARGS_COMMENT}
302	)
303
304	# Set various overrides.  Note that this leads to rather worse PDF
305	# output.  If we ever decide to make xmlto a more likely part of the
306	# process, we probably need to rework things so we generate a
307	# different XML for the manpage path vs. the PDF path...
308	set(OVERRIDE_DTYPE manpage PARENT_SCOPE)
309
310	# This does _very_ poorly [currently?] with DocBook 5 output.
311	if(ASCIIDOCTOR_CAN_DBXML)
312		set(_addg "; downgrading asciidoctor output to docbook45")
313		set(ASCIIDOCTOR_DB_VER 45 PARENT_SCOPE)
314	endif()
315
316	message(WARNING "Using xmlto manpage generation${_addg}.  This "
317		"will compromise the quality of PDF output.")
318endfunction(xmlto_mk_manpage)
319
320
321
322# Build HTML output with asciidoctor
323function(asciidoctor_mk_html OUTFILE ADFILE)
324	# Guard
325	if(NOT ASCIIDOCTOR_CAN_HTML)
326		message(FATAL_ERROR "asciidoctor can't do html")
327	endif()
328
329	_ad_mk_boilerplate(asciidoctor html ${ARGN})
330
331	# Setup the rule
332	add_custom_command(OUTPUT ${OUTFILE}
333		DEPENDS ${dependancies}
334		COMMAND ${ASCIIDOCTOR} -atoc -anumbered -o ${OUTFILE} ${ADFILE}
335		COMMENT ${_ARGS_COMMENT}
336	)
337endfunction(asciidoctor_mk_html)
338
339
340# And the asciidoc HTML
341function(asciidoc_mk_html OUTFILE ADFILE)
342	# Guard
343	if(NOT ASCIIDOC_CAN_HTML)
344		message(FATAL_ERROR "asciidoc can't do html")
345	endif()
346
347	_ad_mk_boilerplate(asciidoc html ${ARGN})
348
349	# Setup the rule
350	add_custom_command(OUTPUT ${OUTFILE}
351		DEPENDS ${dependancies}
352		COMMAND ${ASCIIDOC} -atoc -anumbered -o ${OUTFILE} ${ADFILE}
353		COMMENT ${_ARGS_COMMENT}
354	)
355endfunction(asciidoc_mk_html)
356
357
358# Building DocBook XML
359function(asciidoctor_mk_docbook OUTFILE ADFILE)
360	# Guard
361	if(NOT ASCIIDOCTOR_CAN_DBXML)
362		message(FATAL_ERROR "asciidoctor can't do DocBook")
363	endif()
364
365	_ad_mk_boilerplate(asciidoctor docbook ${ARGN})
366
367	set(DTYPE article)
368	if(OVERRIDE_DTYPE)
369		set(DTYPE ${OVERRIDE_DTYPE})
370	endif()
371
372	add_custom_command(OUTPUT ${OUTFILE}
373		DEPENDS ${dependancies}
374		COMMAND ${ASCIIDOCTOR} -b docbook${ASCIIDOCTOR_DB_VER}
375			-d ${DTYPE} -o ${OUTFILE} ${ADFILE}
376		COMMENT "${_ARGS_COMMENT} (docbook${ASCIIDOCTOR_DB_VER},${DTYPE})"
377	)
378endfunction(asciidoctor_mk_docbook)
379
380function(asciidoc_mk_docbook OUTFILE ADFILE)
381	# Guard
382	if(NOT ASCIIDOC_CAN_DBXML)
383		message(FATAL_ERROR "asciidoc can't do DocBook")
384	endif()
385
386	_ad_mk_boilerplate(asciidoc docbook ${ARGN})
387
388	set(DTYPE article)
389	if(OVERRIDE_DTYPE)
390		set(DTYPE ${OVERRIDE_DTYPE})
391	endif()
392
393	add_custom_command(OUTPUT ${OUTFILE}
394		DEPENDS ${dependancies}
395		COMMAND ${ASCIIDOC} -b docbook${ASCIIDOC_DB_VER}
396			-d ${DTYPE} -o ${OUTFILE} ${ADFILE}
397		COMMENT "${_ARGS_COMMENT} (docbook${ASCIIDOC_DB_VER},${DTYPE})"
398	)
399endfunction(asciidoc_mk_docbook)
400
401
402# PDF via dblatex
403function(dblatex_mk_pdf OUTFILE XMLFILE)
404	if(NOT DBLATEX_CAN_PDF)
405		message(FATAL_ERROR "dblatex can't do PDF")
406	endif()
407
408	_ad_mk_boilerplate(dblatex pdf ${ARGN})
409
410	# Passes through to LaTeX geometry.
411	# Likely choices: letterpaper, a4paper
412	if(NOT DBLATEX_PAPERSIZE)
413		set(DBLATEX_PAPERSIZE "a4paper")
414	endif()
415
416	add_custom_command(OUTPUT ${OUTFILE}
417		DEPENDS ${XMLFILE} ${dependancies}
418		COMMAND ${DBLATEX}
419			-tpdf
420			-Pdoc.collab.show=0
421			-Platex.output.revhistory=0
422			-Ppaper.type=${DBLATEX_PAPERSIZE}
423			-Ppage.margin.top=2cm
424			-Ppage.margin.bottom=2cm
425			-o ${OUTFILE} ${XMLFILE}
426		COMMENT ${_ARGS_COMMENT}
427	)
428endfunction(dblatex_mk_pdf)
429