1# ============================================================================
2# Copyright (c) 2011-2012 University of Pennsylvania
3# Copyright (c) 2013-2014 Carnegie Mellon University
4# Copyright (c) 2013-2016 Andreas Schuh
5# All rights reserved.
6#
7# See COPYING file for license information or visit
8# https://cmake-basis.github.io/download.html#license
9# ============================================================================
10
11##############################################################################
12# @file  DocTools.cmake
13# @brief Tools related to gnerating or adding software documentation.
14#
15# @ingroup CMakeTools
16##############################################################################
17
18if (__BASIS_DOCTOOLS_INCLUDED)
19  return ()
20else ()
21  set (__BASIS_DOCTOOLS_INCLUDED TRUE)
22endif ()
23
24
25# ============================================================================
26# adding / generating documentation
27# ============================================================================
28
29# ----------------------------------------------------------------------------
30## @brief Add documentation target.
31#
32# This function is used to add a software documentation files to the project
33# which are either just copied to the installation or generated from input
34# files such as in particular source code files and documentation files
35# marked up using one of the supported lightweight markup languages.
36#
37# The supported generators are:
38# <table border="0">
39#   <tr>
40#     @tp @b None @endtp
41#     <td>This generator simply installs the given file or all files within
42#         the specified directory.</td>
43#   </tr>
44#   <tr>
45#     @tp @b Doxygen @endtp
46#     <td>Used to generate API documentation from in-source code comments and
47#         other related files marked up using Doxygen comments. See
48#         basis_add_doxygen_doc() for more details.</td>
49#   </tr>
50#   <tr>
51#     @tp @b Sphinx @endtp
52#     <td>Used to generate documentation such as a web site from reStructuredText.
53#         See basis_add_sphinx_doc() for more details.</td>
54#   </tr>
55# </table>
56#
57# @param [in] TARGET_NAME Name of the documentation target or file.
58# @param [in] ARGN        Documentation generator as "GENERATOR generator" option
59#                         and additional arguments for the particular generator.
60#                         The case of the generator name is ignored, i.e.,
61#                         @c Doxygen, @c DOXYGEN, @c doxYgen are all valid arguments
62#                         which select the @c Doxygen generator. The default generator
63#                         is the @c None generator.</td>
64#
65# @returns Adds a custom target @p TARGET_NAME for the generation of the
66#          documentation.
67#
68# @sa basis_install_doc()
69# @sa basis_add_doxygen_doc()
70# @sa basis_add_sphinx_doc()
71#
72# @ingroup CMakeAPI
73function (basis_add_doc TARGET_NAME)
74  CMAKE_PARSE_ARGUMENTS (ARGN "" "GENERATOR" "" ${ARGN})
75  if (NOT ARGN_GENERATOR)
76    set (ARGN_GENERATOR "NONE")
77  else ()
78    string (TOUPPER "${ARGN_GENERATOR}" ARGN_GENERATOR)
79  endif ()
80  if (ARGN_GENERATOR MATCHES "NONE")
81    basis_install_doc (${TARGET_NAME} ${ARGN_UNPARSED_ARGUMENTS})
82  elseif (ARGN_GENERATOR MATCHES "DOXYGEN")
83    basis_add_doxygen_doc (${TARGET_NAME} ${ARGN_UNPARSED_ARGUMENTS})
84  elseif (ARGN_GENERATOR MATCHES "SPHINX")
85    basis_add_sphinx_doc (${TARGET_NAME} ${ARGN_UNPARSED_ARGUMENTS})
86  else ()
87    message (FATAL_ERROR "Unknown documentation generator: ${ARGN_GENERATOR}.")
88  endif ()
89endfunction ()
90
91# ----------------------------------------------------------------------------
92## @brief Install documentation file(s).
93#
94# This function either adds an installation rule for a single documentation
95# file or a directory containing multiple documentation files.
96#
97# Example:
98# @code
99# basis_install_doc ("User Manual.pdf" OUTPUT_NAME "BASIS User Manual.pdf")
100# basis_install_doc (DeveloperManual.docx COMPONENT dev)
101# basis_install_doc (SourceManual.html    COMPONENT src)
102# @endcode
103#
104# @param [in] SOURCE Documentation file or directory to install.
105# @param [in] ARGN   List of optional arguments. Valid arguments are:
106# @par
107# <table border="0">
108#   <tr>
109#     @tp @b COMPONENT component @endtp
110#     <td>Name of the component this documentation belongs to.
111#         Defaults to @c BASIS_RUNTIME_COMPONENT.</td>
112#   </tr>
113#   <tr>
114#     @tp @b DESTINATION dir @endtp
115#     <td>Installation directory prefix. Defaults to @c INSTALL_DOC_DIR.</td>
116#   </tr>
117#   <tr>
118#     @tp @b OUTPUT_NAME name @endtp
119#     <td>Name of file or directory after installation.</td>
120#   </tr>
121# </table>
122#
123# @sa basis_add_doc()
124function (basis_install_doc SOURCE)
125  CMAKE_PARSE_ARGUMENTS (ARGN "" "COMPONENT;DESTINATION;OUTPUT_NAME" "" ${ARGN})
126
127  if (NOT IS_ABSOLUTE "${SOURCE}")
128    get_filename_component (SOURCE "${SOURCE}" ABSOLUTE)
129  endif ()
130  if (NOT ARGN_DESTINATION)
131    set (ARGN_DESTINATION "${INSTALL_DOC_DIR}")
132  endif ()
133  if (NOT ARGN_COMPONENT)
134    set (ARGN_COMPONENT "${BASIS_RUNTIME_COMPONENT}")
135  endif ()
136  if (NOT ARGN_COMPONENT)
137    set (ARGN_COMPONENT "Unspecified")
138  endif ()
139  if (NOT ARGN_OUTPUT_NAME)
140    basis_get_filename_component (ARGN_OUTPUT_NAME "${SOURCE}" NAME)
141  endif ()
142
143  basis_get_relative_path (
144    RELPATH
145      "${CMAKE_SOURCE_DIR}"
146      "${CMAKE_CURRENT_SOURCE_DIR}/${ARGN_OUTPUT_NAME}"
147  )
148
149  message (STATUS "Adding documentation ${RELPATH}...")
150
151  if (IS_DIRECTORY "${SOURCE}")
152    basis_install_directory (
153      "${SOURCE}" "${ARGN_DESTINATION}/${ARGN_OUTPUT_NAME}"
154      COMPONENT "${ARGN_COMPONENT}"
155    )
156  else ()
157    install (
158      FILES       "${SOURCE}"
159      DESTINATION "${ARGN_DESTINATION}"
160      COMPONENT   "${ARGN_COMPONENT}"
161      RENAME      "${ARGN_OUTPUT_NAME}"
162    )
163  endif ()
164
165  message (STATUS "Adding documentation ${RELPATH}... - done")
166endfunction ()
167
168# ----------------------------------------------------------------------------
169## @brief Add documentation to be generated by Doxygen.
170#
171# This function adds a build target to generate documentation from in-source
172# code comments and other related project pages using
173# <a href="http://www.stack.nl/~dimitri/doxygen/index.html">Doxygen</a>.
174#
175# @param [in] TARGET_NAME Name of the documentation target.
176# @param [in] ARGN        List of arguments. The valid arguments are:
177# @par
178# <table border="0">
179#   <tr>
180#     @tp @b EXCLUDE_FROM_DOC @endtp
181#     <td>By default, the specified target is build as part of the global
182#         @c doc target. If this option is given, however, the added
183#         documentation will not be build as part of this target.</td>
184#   </tr>
185#   <tr>
186#     @tp @b COMPONENT component @endtp
187#     <td>Name of the component this documentation belongs to.
188#         Defaults to @c BASIS_LIBRARY_COMPONENT.</td>
189#   </tr>
190#   <tr>
191#     @tp @b DESTINATION dir @endtp
192#     <td>Installation directory prefix. Defaults to
193#         @c BASIS_INSTALL_&ltTARGET&gt;_DIR in case of HTML output if set.
194#         Otherwise, the generated HTML files are not installed.</td>
195#   </tr>
196#   <tr>
197#     @tp @b DOXYFILE file @endtp
198#     <td>Name of the template Doxyfile.</td>
199#   </tr>
200#   <tr>
201#     @tp @b PROJECT_NAME name @endtp
202#     <td>Value for Doxygen's @c PROJECT_NAME tag which is used to
203#         specify the project name.@n
204#         Default: @c PROJECT_NAME.</td>
205#   </tr>
206#   <tr>
207#     @tp @b PROJECT_NUMBER version @endtp
208#     <td>Value for Doxygen's @c PROJECT_NUMBER tag which is used
209#         to specify the project version number.@n
210#         Default: @c PROJECT_RELEASE.</td>
211#   </tr>
212#   <tr>
213#     @tp @b PROJECT_WEBSITE url @endtp
214#     <td>Used for links to project website.@n
215#         Default: @c PROJECT_PACKAGE_WEBSITE </td>
216#   </tr>
217#   <tr>
218#     @tp @b INPUT path1 [path2 ...] @endtp
219#     <td>Value for Doxygen's @c INPUT tag which is used to specify input
220#         directories/files. Any given input path is added to the default
221#         input paths.@n
222#         Default: @c PROJECT_CODE_DIRS, @c BINARY_CODE_DIR,
223#                  @c PROJECT_INCLUDE_DIRS, @c BINARY_INCLUDE_DIR.</td>
224#   </tr>
225#   <tr>
226#     @tp @b EXCLUDE_BASIS_MODULES @endtp
227#     <td>Do not add project CMake files used and generated by BASIS to @b INPUT.</td>
228#   </tr>
229#   <tr>
230#     @tp @b EXCLUDE_BASIS_UTILITIES @endtp
231#     <td>Do not add documentation (.dox) files for used CMake BASIS Utilities to @b INPUT.</td>
232#   </tr>
233#   <tr>
234#     @tp @b INPUT_FILTER filter @endtp
235#     <td>
236#       Value for Doxygen's @c INPUT_FILTER tag which can be used to
237#       specify a default filter for all input files.@n
238#       Default: @c doxyfilter of BASIS.
239#     </td>
240#   </tr>
241#   <tr>
242#     @tp @b FILTER_PATTERNS pattern1 [pattern2...] @endtp
243#     <td>Value for Doxygen's @c FILTER_PATTERNS tag which can be used to
244#         specify filters on a per file pattern basis.@n
245#         Default: None.</td>
246#   </tr>
247#   <tr>
248#     @tp @b INCLUDE_PATH path1 [path2...] @endtp
249#     <td>Doxygen's @c INCLUDE_PATH tag can be used to specify one or more
250#         directories that contain include files that are not input files
251#         but should be processed by the preprocessor. Any given directories
252#         are appended to the default include path considered.
253#         Default: Directories added by basis_include_directories().</td>
254#   </tr>
255#   <tr>
256#     @tp @b EXCLUDE_PATTERNS pattern1 [pattern2 ...] @endtp
257#     <td>Additional patterns used for Doxygen's @c EXCLUDE_PATTERNS tag
258#         which can be used to specify files and/or directories that
259#         should be excluded from the INPUT source files.@n
260#         Default: No exclude patterns.</td>
261#   </tr>
262#   <tr>
263#     @tp @b PREDEFINED name1|name1=value1 [name2|name2=value2...] @endtp
264#     <td>Add preprocessor definitions to be expanded by Doxygen.</td>
265#   </tr>
266#   <tr>
267#     @tp @b OUTPUT fmt @endtp
268#     <td>Specify output formats in which to generate the documentation.
269#         Currently, only @c html and @c xml are supported.</td>
270#   </tr>
271#   <tr>
272#     @tp @b OUTPUT_DIRECTORY dir @endtp
273#     <td>Value for Doxygen's @c OUTPUT_DIRECTORY tag which can be used to
274#         specify the output directory. The output files are written to
275#         subdirectories named "html", "latex", "rtf", and "man".@n
276#         Default: <tt>CMAKE_CURRENT_BINARY_DIR/TARGET_NAME</tt>.</td>
277#   </tr>
278#   <tr>
279#     @tp @b COLS_IN_ALPHA_INDEX n @endtp
280#     <td>Number of columns in alphabetical index. Default: 3.</td>
281#   </tr>
282#   <tr>
283#     @tp @b IGNORE_PREFIX prefix1 [prefix2...] @endtp
284#     <td>In case all classes in a project start with a common prefix, all
285#         classes will be put under the same header in the alphabetical index.
286#         The IGNORE_PREFIX tag can be used to specify one or more prefixes that
287#         should be ignored while generating the index headers.</td>
288#   </tr>
289#   <tr>
290#     @tp @b PROVIDER_NAME name @endtp
291#     <td>Value for provider name, such as a company name,
292#         that will be used for pages in the doxygen output.@n
293#         Default: @c PROJECT_PROVIDER_NAME.</td>
294#   </tr>
295#   <tr>
296#     @tp @b PROVIDER_WEBSITE url @endtp
297#     <td>Value for provider website, such as a company website,
298#         that will be used for pages in the doxygen output.@n
299#         Default: @c PROJECT_PROVIDER_WEBSITE.</td>
300#   </tr>
301#   <tr>
302#     @tp @b PROVIDER_LOGO image_file @endtp
303#     <td>Value for provider logo file, such as a company logo,
304#         that will be used for pages in the doxygen output.@n
305#         Default: @c PROJECT_PROVIDER_LOGO.</td>
306#   </tr>
307#   <tr>
308#     @tp @b DIVISION_NAME name @endtp
309#     <td>Value for division name, such as a company division name,
310#         that will be used for pages in the doxygen output.@n
311#         Default: @c PROJECT_DIVISION_NAME.</td>
312#   </tr>
313#   <tr>
314#     @tp @b DIVISION_WEBSITE url @endtp
315#     <td>Value for division website, such as a company division website,
316#         that will be used for pages in the doxygen output.@n
317#         Default: @c PROJECT_DIVISION_WEBSITE.</td>
318#   </tr>
319#   <tr>
320#     @tp @b DIVISION_LOGO image_file @endtp
321#     <td>Value for division logo file, such as a company division logo,
322#         that will be used for pages in the doxygen output.@n
323#         Default: @c PROJECT_DIVISION_LOGO.</td>
324#   </tr>
325#   <tr>
326#     @tp @b ENABLED_SECTIONS section1 [section2 ...] @endtp
327#     <td>The ENABLED_SECTIONS tag can be used to enable conditional
328#         documentation sections, marked by "\if sectionname ... \endif".</td>
329#   </tr>
330#   <tr>
331#     @tp @b HTML_HEADER html_file @endtp
332#     <td>The HTML_HEADER tag can be used to specify a personal HTML header for
333#         each generated HTML page. If none specified, the
334#         @c "PROJECT_SOURCE_DIR/doc/doxygen_header.html(.in)?" file is used if present.
335#         Otherwise, a default header is used. Specify the value @c Doxygen to use the
336#         standard header generated by Doxygen instead.</td>
337#   </tr>
338#   <tr>
339#     @tp @b HTML_FOOTER html_file @endtp
340#     <td>The HTML_FOOTER tag can be used to specify a personal HTML footer for
341#         each generated HTML page. If none specified, the
342#         @c "PROJECT_SOURCE_DIR/doc/doxygen_footer.html(.in)?" file is used if present.
343#         Otherwise, a default footer is used. Specify the value @c Doxygen to use the
344#         standard footer generated by Doxygen instead.</td>
345#   </tr>
346#   <tr>
347#     @tp @b HTML_EXTRA_STYLESHEET css_file @endtp
348#     <td>The HTML_EXTRA_STYLESHEET tag can be used to specify a user-defined cascading
349#         style sheet that is used by each HTML page. It can be used to
350#         fine-tune the look of the HTML output. If none specified, the
351#         @c "PROJECT_SOURCE_DIR/doc/doxygen_extra.css(.in)?" file is used if present.</td>
352#   </tr>
353#   <tr>
354#     @tp @b HTML_EXTRA_FILES file1 [file2...] @endtp
355#     <td>The HTML_EXTRA_FILES tag can be used to specify additional files needed
356#         for the HTML output of the API documentation.</td>
357#   </tr>
358#   <tr>
359#     @tp @b DISABLE_PROJECT_NAME_DISPLAY@endtp
360#     <td>The DISABLE_PROJECT_NAME_DISPLAY option causes Doxygen's
361#         @c PROJECT_NAME text not to be displayed in the header.
362#         Use this if the project name is already part of the logo
363#         so it won't be there twice in the logo image and title text.</td>
364#   </tr>
365# </table>
366# @n
367# See <a href="http://www.stack.nl/~dimitri/doxygen/manual/config.html">here</a> for a
368# documentation of the Doxygen tags.
369# @n@n
370# Example:
371# @code
372# basis_add_doxygen_doc (
373#   apidoc
374#   DOXYFILE        "Doxyfile.in"
375#   PROJECT_NAME    "${PROJECT_NAME}"
376#   PROJECT_VERSION "${PROJECT_VERSION}"
377#   COMPONENT       dev
378# )
379# @endcode
380#
381# @sa basis_add_doc()
382function (basis_add_doxygen_doc TARGET_NAME)
383  # check target name
384  basis_check_target_name ("${TARGET_NAME}")
385  basis_make_target_uid (TARGET_UID "${TARGET_NAME}")
386  string (TOLOWER "${TARGET_NAME}" TARGET_NAME_L)
387  string (TOUPPER "${TARGET_NAME}" TARGET_NAME_U)
388  # verbose output
389  message (STATUS "Adding documentation ${TARGET_UID}...")
390  # find Doxygen
391  find_package (Doxygen QUIET)
392  if (NOT DOXYGEN_EXECUTABLE)
393    if (BUILD_DOCUMENTATION)
394      message (FATAL_ERROR "Doxygen not found! Either install Doxygen and/or set DOXYGEN_EXECUTABLE or disable BUILD_DOCUMENTATION.")
395    endif ()
396    message (STATUS "Doxygen not found. Generation of ${TARGET_UID} documentation disabled.")
397    message (STATUS "Adding documentation ${TARGET_UID}... - skipped")
398    return ()
399  endif ()
400  # parse arguments
401  set (VALUEARGS
402    PROJECT_NAME
403    PROJECT_NUMBER
404    PROJECT_WEBSITE
405    PROVIDER_NAME
406    PROVIDER_WEBSITE
407    DIVISION_NAME
408    DIVISION_WEBSITE
409    COMPONENT
410    DESTINATION
411    HTML_DESTINATION
412    MAN_DESTINATION
413    OUTPUT_DIRECTORY
414    COLS_IN_ALPHA_INDEX
415    MAN_SECTION
416  )
417  set (OPTIONAL_FILE_OPTIONS
418    HTML_FOOTER
419    HTML_HEADER
420    HTML_EXTRA_STYLESHEET
421    PROJECT_LOGO
422    PROVIDER_LOGO
423    DIVISION_LOGO
424    DOXYFILE
425    TAGFILE
426  )
427  CMAKE_PARSE_ARGUMENTS (
428    DOXYGEN
429      "EXCLUDE_FROM_DOC;DISABLE_PROJECT_NAME_DISPLAY;EXCLUDE_BASIS_MODULES;EXCLUDE_BASIS_UTILITIES"
430      "${VALUEARGS};${OPTIONAL_FILE_OPTIONS}"
431      "INPUT;OUTPUT;INPUT_FILTER;FILTER_PATTERNS;EXCLUDE_PATTERNS;INCLUDE_PATH;IGNORE_PREFIX;ENABLED_SECTIONS;PREDEFINED;HTML_EXTRA_FILES"
432      ${ARGN}
433  )
434  unset (VALUEARGS)
435  # handle special arguments
436  set (DOXYGEN_HTML_HEADER_IS_DEFAULT FALSE)
437  if (DOXYGEN_HTML_HEADER MATCHES "^(Doxygen|doxygen|DOXYGEN|none|None|NONE)$")
438    set (DOXYGEN_HTML_HEADER)
439  elseif (NOT DOXYGEN_HTML_HEADER OR DOXYGEN_HTML_HEADER MATCHES "^(Default|default|DEFAULT)$")
440    set (DOXYGEN_HTML_HEADER "${BASIS_MODULE_PATH}/doxygen_header.html.in")
441    set (DOXYGEN_HTML_HEADER_IS_DEFAULT TRUE)
442  endif ()
443  if (DOXYGEN_HTML_FOOTER MATCHES "^(Doxygen|doxygen|DOXYGEN|none|None|NONE)$")
444    set (DOXYGEN_HTML_FOOTER)
445  elseif (NOT DOXYGEN_HTML_FOOTER OR DOXYGEN_HTML_FOOTER MATCHES "^(Default|default|DEFAULT)$")
446    set (DOXYGEN_HTML_FOOTER "${BASIS_MODULE_PATH}/doxygen_footer.html.in")
447  endif ()
448  # make file paths absolute and check if files exist
449  foreach (opt IN LISTS OPTIONAL_FILE_OPTIONS)
450    if (DOXYGEN_${opt})
451      get_filename_component (DOXYGEN_${opt} "${DOXYGEN_${opt}}" ABSOLUTE)
452      if (NOT EXISTS "${DOXYGEN_${opt}}")
453        message (FATAL_ERROR "File ${DOXYGEN_${opt}} does not exist. Check value of the ${opt} option and make sure the file is present.")
454      endif ()
455    endif ()
456  endforeach ()
457  set (TMP_DOXYGEN_HTML_EXTRA_FILES)
458  foreach (path IN LISTS DOXYGEN_HTML_EXTRA_FILES)
459    get_filename_component (abspath "${path}" ABSOLUTE)
460    if (NOT EXISTS "${path}")
461      message (FATAL_ERROR "File ${path} does not exist. Check value of the HTML_EXTRA_FILES option and make sure the file is present.")
462    endif ()
463    list (APPEND TMP_DOXYGEN_HTML_EXTRA_FILES "${path}")
464  endforeach ()
465  set (DOXYGEN_HTML_EXTRA_FILES "${TMP_DOXYGEN_HTML_EXTRA_FILES}")
466  unset (TMP_DOXYGEN_HTML_EXTRA_FILES)
467  # default component
468  if (NOT DOXYGEN_COMPONENT)
469    set (DOXYGEN_COMPONENT "${BASIS_LIBRARY_COMPONENT}")
470  endif ()
471  if (NOT DOXYGEN_COMPONENT)
472    set (DOXYGEN_COMPONENT "Unspecified")
473  endif ()
474  # configuration file
475  if (NOT DOXYGEN_DOXYFILE)
476    set (DOXYGEN_DOXYFILE "${BASIS_DOXYGEN_DOXYFILE}")
477  endif ()
478  if (NOT EXISTS "${DOXYGEN_DOXYFILE}")
479    message (FATAL_ERROR "Missing option DOXYGEN_FILE or Doxyfile ${DOXYGEN_DOXYFILE} does not exist.")
480  endif ()
481  # default project attributes and logos
482  if (NOT DOXYGEN_PROJECT_NAME)
483    set (DOXYGEN_PROJECT_NAME "${PROJECT_NAME}")
484  endif ()
485  if (NOT DOXYGEN_PROJECT_NUMBER)
486    set (DOXYGEN_PROJECT_NUMBER "${PROJECT_RELEASE}")
487  endif ()
488  if (NOT DOXYGEN_PROJECT_LOGO)
489    set (DOXYGEN_PROJECT_LOGO "${PROJECT_PACKAGE_LOGO}")
490  elseif (DOXYGEN_PROJECT_LOGO MATCHES "^None|none|NONE$")
491    set (DOXYGEN_PROJECT_LOGO)
492  endif ()
493  if (NOT DOXYGEN_PROJECT_WEBSITE)
494    set (DOXYGEN_PROJECT_WEBSITE "${PROJECT_PACKAGE_WEBSITE}")
495  endif ()
496  if (NOT DOXYGEN_PROVIDER_NAME)
497    set (DOXYGEN_PROVIDER_NAME "${PROJECT_PROVIDER_NAME}")
498  endif ()
499  if (NOT DOXYGEN_PROVIDER_WEBSITE)
500    set (DOXYGEN_PROVIDER_WEBSITE "${PROJECT_PROVIDER_WEBSITE}")
501  endif ()
502  if (NOT DOXYGEN_PROVIDER_LOGO)
503    set (DOXYGEN_PROVIDER_LOGO "${PROJECT_PROVIDER_LOGO}")
504  endif ()
505  if (NOT DOXYGEN_DIVISION_NAME)
506    set (DOXYGEN_DIVISION_NAME "${PROJECT_DIVISION_NAME}")
507  endif ()
508  if (NOT DOXYGEN_DIVISION_WEBSITE)
509    set (DOXYGEN_DIVISION_WEBSITE "${PROJECT_DIVISION_WEBSITE}")
510  endif ()
511  if (NOT DOXYGEN_DIVISION_LOGO)
512    set (DOXYGEN_DIVISION_LOGO "${PROJECT_DIVISION_LOGO}")
513  endif ()
514  # set visibility property of project logos
515  if (DOXYGEN_PROJECT_LOGO)
516    set (DOXYGEN_PROJECT_LOGO_DISPLAY "block")
517  else ()
518    set (DOXYGEN_PROJECT_LOGO_DISPLAY "none")
519  endif ()
520  if (DOXYGEN_PROVIDER_LOGO)
521    set (DOXYGEN_PROVIDER_LOGO_DISPLAY "inline")
522  else ()
523    set (DOXYGEN_PROVIDER_LOGO_DISPLAY "block")
524  endif ()
525  if (DOXYGEN_DIVISION_LOGO)
526    set (DOXYGEN_DIVISION_LOGO_DISPLAY "inline")
527  else ()
528    set (DOXYGEN_DIVISION_LOGO_DISPLAY "block")
529  endif ()
530  # allow the user to disable the text header if desired
531  if(DOXYGEN_DISABLE_PROJECT_NAME_DISPLAY)
532    set (DOXYGEN_PROJECT_NAME_DISPLAY "none")
533  else()
534    set (DOXYGEN_PROJECT_NAME_DISPLAY "inline")
535  endif()
536  # standard input files
537  if (NOT EXCLUDE_BASIS_MODULES)
538    list (APPEND DOXYGEN_INPUT "${PROJECT_SOURCE_DIR}/BasisProject.cmake")
539    if (EXISTS "${PROJECT_CONFIG_DIR}/Depends.cmake")
540      list (APPEND DOXYGEN_INPUT "${PROJECT_CONFIG_DIR}/Depends.cmake")
541    endif ()
542    if (EXISTS "${BINARY_CONFIG_DIR}/Directories.cmake")
543      list (APPEND DOXYGEN_INPUT "${BINARY_CONFIG_DIR}/Directories.cmake")
544    endif ()
545    if (EXISTS "${BINARY_CONFIG_DIR}/BasisSettings.cmake")
546      list (APPEND DOXYGEN_INPUT "${BINARY_CONFIG_DIR}/BasisSettings.cmake")
547    endif ()
548    if (EXISTS "${BINARY_CONFIG_DIR}/ProjectSettings.cmake")
549      list (APPEND DOXYGEN_INPUT "${BINARY_CONFIG_DIR}/ProjectSettings.cmake")
550    endif ()
551    if (EXISTS "${BINARY_CONFIG_DIR}/Settings.cmake")
552      list (APPEND DOXYGEN_INPUT "${BINARY_CONFIG_DIR}/Settings.cmake")
553    elseif (EXISTS "${PROJECT_CONFIG_DIR}/Settings.cmake")
554      list (APPEND DOXYGEN_INPUT "${PROJECT_CONFIG_DIR}/Settings.cmake")
555    endif ()
556    if (EXISTS "${BASIS_SCRIPT_CONFIG_FILE}")
557      list (APPEND DOXYGEN_INPUT "${BASIS_SCRIPT_CONFIG_FILE}")
558    endif ()
559    if (EXISTS "${BINARY_CONFIG_DIR}/ScriptConfig.cmake")
560      list (APPEND DOXYGEN_INPUT "${BINARY_CONFIG_DIR}/ScriptConfig.cmake")
561    endif ()
562    if (EXISTS "${PROJECT_CONFIG_DIR}/ConfigSettings.cmake")
563      list (APPEND DOXYGEN_INPUT "${PROJECT_CONFIG_DIR}/ConfigSettings.cmake")
564    endif ()
565    if (EXISTS "${PROJECT_SOURCE_DIR}/CTestConfig.cmake")
566      list (APPEND DOXYGEN_INPUT "${PROJECT_SOURCE_DIR}/CTestConfig.cmake")
567    endif ()
568    if (EXISTS "${PROJECT_BINARY_DIR}/CTestCustom.cmake")
569      list (APPEND DOXYGEN_INPUT "${PROJECT_BINARY_DIR}/CTestCustom.cmake")
570    endif ()
571    # package configuration files - only exist *after* this function executed
572    list (APPEND DOXYGEN_INPUT "${BINARY_LIBCONF_DIR}/${PROJECT_PACKAGE_CONFIG_PREFIX}Config.cmake")
573    list (APPEND DOXYGEN_INPUT "${BINARY_LIBCONF_DIR}/${PROJECT_PACKAGE_CONFIG_PREFIX}ConfigVersion.cmake")
574    list (APPEND DOXYGEN_INPUT "${BINARY_LIBCONF_DIR}/${PROJECT_PACKAGE_CONFIG_PREFIX}Use.cmake")
575    # add .dox files with definition of BASIS Modules groups
576    if (BASIS_DIR)
577      list (APPEND DOXYGEN_INPUT "${BASIS_MODULE_PATH}/Modules.dox")
578    endif ()
579  endif ()
580  # input directories
581  foreach (_DIR IN LISTS BINARY_INCLUDE_DIR PROJECT_INCLUDE_DIRS BINARY_CODE_DIR PROJECT_CODE_DIRS)
582    if (IS_DIRECTORY ${_DIR})
583      list (APPEND DOXYGEN_INPUT "${_DIR}")
584    endif ()
585  endforeach ()
586  foreach (M IN LISTS PROJECT_MODULES_ENABLED)
587    foreach (_DIR IN LISTS ${M}_INCLUDE_DIRS ${M}_CODE_DIRS)
588      if (IS_DIRECTORY ${_DIR})
589        list (APPEND DOXYGEN_INPUT "${_DIR}")
590      endif ()
591    endforeach ()
592  endforeach ()
593  # in case of scripts, have Doxygen process the configured versions for the
594  # installation which are further located in proper subdirectories instead
595  # of the original source files
596  basis_get_project_property (TARGETS)
597  foreach (T IN LISTS TARGETS)
598    get_target_property (BASIS_TYPE ${T} BASIS_TYPE)
599    get_target_property (IS_TEST    ${T} TEST)
600    if (NOT IS_TEST AND BASIS_TYPE MATCHES "SCRIPT")
601      get_target_property (SOURCES ${T} SOURCES)
602      if (SOURCES)
603        list (GET SOURCES 0 BUILD_DIR) # CMake <3.1 stores path to internal build directory here
604        if (BUILD_DIR MATCHES "CMakeFiles")
605          list (REMOVE_AT SOURCES 0)
606        endif ()
607        get_target_property (BUILD_DIR ${T} BUILD_DIRECTORY)
608        list (APPEND DOXYGEN_INPUT "${BUILD_DIR}.dir/install")
609        foreach (S IN LISTS SOURCES)
610          list (APPEND DOXYGEN_EXCLUDE_PATTERNS "${S}")
611          list (APPEND DOXYGEN_EXCLUDE_PATTERNS "${BUILD_DIR}.dir/build")
612        endforeach ()
613      endif ()
614    endif ()
615  endforeach ()
616  # add .dox files as input
617  file (GLOB_RECURSE DOX_FILES "${PROJECT_DOC_DIR}/*.dox")
618  list (SORT DOX_FILES) # alphabetic order
619  list (APPEND DOXYGEN_INPUT ${DOX_FILES})
620  # add .dox files of used BASIS utilities
621  if (BASIS_DIR AND NOT EXCLUDE_BASIS_UTILITIES)
622    list (APPEND DOXYGEN_INPUT "${BASIS_MODULE_PATH}/Utilities.dox")
623    list (APPEND DOXYGEN_INPUT "${BASIS_MODULE_PATH}/CxxUtilities.dox")
624    foreach (L IN ITEMS Cxx Java Python Perl Bash Matlab)
625      string (TOUPPER "${L}" U)
626      basis_get_project_property (USES_${U}_UTILITIES PROPERTY PROJECT_USES_${U}_UTILITIES)
627      if (USES_${U}_UTILITIES)
628        list (FIND DOXYGEN_INPUT "${BASIS_MODULE_PATH}/Utilities.dox" IDX)
629        if (IDX EQUAL -1)
630          list (APPEND DOXYGEN_INPUT "${BASIS_MODULE_PATH}/Utilities.dox")
631        endif ()
632        list (APPEND DOXYGEN_INPUT "${BASIS_MODULE_PATH}/${L}Utilities.dox")
633      endif ()
634    endforeach ()
635  endif ()
636  # include path - Disabled as this increases the runtime of Doxygen but
637  #                generally the source of third-party packages are not
638  #                really referenced. Only the source files of this
639  #                project have to be considered. This code is kept as it
640  #                might be used again at a later point once it is figured
641  #                how Doxygen can be only rerun if necessary.
642  #basis_get_project_property (INCLUDE_DIRS PROPERTY PROJECT_INCLUDE_DIRS)
643  #foreach (D IN LISTS INCLUDE_DIRS)
644  #  list (FIND DOXYGEN_INPUT "${D}" IDX)
645  #  if (IDX EQUAL -1)
646  #    list (APPEND DOXYGEN_INCLUDE_PATH "${D}")
647  #  endif ()
648  #endforeach ()
649  #basis_list_to_delimited_string (
650  #  DOXYGEN_INCLUDE_PATH "\"\nINCLUDE_PATH          += \"" ${DOXYGEN_INCLUDE_PATH}
651  #)
652  #set (DOXYGEN_INCLUDE_PATH "\"${DOXYGEN_INCLUDE_PATH}\"")
653  # make string from DOXYGEN_INPUT - after include path was set
654  basis_list_to_delimited_string (
655    DOXYGEN_INPUT "\"\nINPUT                 += \"" ${DOXYGEN_INPUT}
656  )
657  set (DOXYGEN_INPUT "\"${DOXYGEN_INPUT}\"")
658  # preprocessor definitions
659  basis_list_to_delimited_string (
660    DOXYGEN_PREDEFINED "\"\nPREDEFINED            += \"" ${DOXYGEN_PREDEFINED}
661  )
662  set (DOXYGEN_PREDEFINED "\"${DOXYGEN_PREDEFINED}\"")
663  # outputs
664  if (NOT DOXYGEN_OUTPUT_DIRECTORY)
665    set (DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME_L}")
666  endif ()
667  if (DOXYGEN_TAGFILE MATCHES "^(None|NONE|none)$")
668    set (DOXYGEN_TAGFILE)
669  else ()
670    set (DOXYGEN_TAGFILE "${DOXYGEN_OUTPUT_DIRECTORY}/Doxytags.${TARGET_NAME_L}")
671  endif ()
672  if (NOT DOXYGEN_OUTPUT)
673    set (DOXYGEN_OUTPUT html)
674  endif ()
675  foreach (F IN ITEMS HTML XML RTF LATEX MAN)
676    set (DOXYGEN_GENERATE_${F} NO)
677  endforeach ()
678  foreach (f IN LISTS DOXYGEN_OUTPUT)
679    if (NOT f MATCHES "^(html|xml)$")
680      message (FATAL_ERROR "Invalid/Unsupported Doxygen output format: ${f}")
681    endif ()
682    string (TOUPPER "${f}" F)
683    set (DOXYGEN_GENERATE_${F} YES)  # enable generation of this output
684    set (DOXYGEN_${F}_OUTPUT "${f}") # relative output directory
685  endforeach ()
686  # input filters
687  if (NOT DOXYGEN_INPUT_FILTER)
688    basis_get_target_uid (DOXYFILTER "basis.doxyfilter")
689    if (TARGET "${DOXYFILTER}")
690      basis_get_target_location (DOXYGEN_INPUT_FILTER ${DOXYFILTER} ABSOLUTE)
691    else ()
692      basis_get_target_uid (DOXYFILTER "doxyfilter")
693      if (TARGET "${DOXYFILTER}")
694        basis_get_target_location (DOXYGEN_INPUT_FILTER ${DOXYFILTER} ABSOLUTE)
695      endif ()
696    endif ()
697  else ()
698    set (DOXYFILTER)
699  endif ()
700  if (DOXYGEN_INPUT_FILTER)
701    if (WIN32)
702      # Doxygen on Windows (XP, 32-bit) (at least up to version 1.8.0) seems
703      # to have a problem of not calling filters which have a space character
704      # in their file path correctly. The doxyfilter.bat Batch program is used
705      # as a wrapper for the actual filter which is part of the BASIS build.
706      # As this file is in the working directory of Doxygen, it can be
707      # referenced relative to this working directory, i.e., without file paths.
708      # The Batch program itself then calls the actual Doxygen filter with proper
709      # quotes to ensure that spaces in the file path are handled correctly.
710      # The file extension .bat shall distinguish this wrapper script from the actual
711      # doxyfilter.cmd which is generated by BASIS on Windows.
712      configure_file ("${BASIS_MODULE_PATH}/doxyfilter.bat.in" "${DOXYGEN_OUTPUT_DIRECTORY}/doxyfilter.bat" @ONLY)
713      set (DOXYGEN_INPUT_FILTER "doxyfilter.bat")
714    endif ()
715  endif ()
716  basis_list_to_delimited_string (
717    DOXYGEN_FILTER_PATTERNS "\"\nFILTER_PATTERNS       += \"" ${DOXYGEN_FILTER_PATTERNS}
718  )
719  if (DOXYGEN_FILTER_PATTERNS)
720    set (DOXYGEN_FILTER_PATTERNS "\"${DOXYGEN_FILTER_PATTERNS}\"")
721  endif ()
722  # exclude patterns
723  list (APPEND DOXYGEN_EXCLUDE_PATTERNS "cmake_install.cmake")
724  list (APPEND DOXYGEN_EXCLUDE_PATTERNS "CTestTestfile.cmake")
725  basis_list_to_delimited_string (
726    DOXYGEN_EXCLUDE_PATTERNS "\"\nEXCLUDE_PATTERNS      += \"" ${DOXYGEN_EXCLUDE_PATTERNS}
727  )
728  set (DOXYGEN_EXCLUDE_PATTERNS "\"${DOXYGEN_EXCLUDE_PATTERNS}\"")
729  # section for man pages
730  if (NOT DOXYGEN_MAN_SECTION)
731    set (DOXYGEN_MAN_SECTION 3)
732  endif ()
733  # other settings
734  if (NOT DOXYGEN_COLS_IN_ALPHA_INDEX OR DOXYGEN_COLS_IN_ALPHA_INDEX MATCHES "[^0-9]")
735    set (DOXYGEN_COLS_IN_ALPHA_INDEX 1)
736  endif ()
737  basis_list_to_delimited_string (DOXYGEN_IGNORE_PREFIX " " ${DOXYGEN_IGNORE_PREFIX})
738  # click & jump in emacs and Visual Studio
739  if (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)")
740    set (DOXYGEN_WARN_FORMAT "\"$file($line) : $text \"")
741  else ()
742    set (DOXYGEN_WARN_FORMAT "\"$file:$line: $text \"")
743  endif ()
744  # installation directories
745  set (BASIS_INSTALL_${TARGET_NAME_U}_DIR "" CACHE PATH "Installation directory for Doxygen ${TARGET_NAME} target.")
746  mark_as_advanced (BASIS_INSTALL_${TARGET_NAME_U}_DIR)
747  foreach (f IN LISTS DOXYGEN_OUTPUT)
748    string (TOUPPER "${f}" F)
749    if (BASIS_INSTALL_${TARGET_NAME_U}_DIR)
750      set (DOXYGEN_${F}_DESTINATION "${BASIS_INSTALL_${TARGET_NAME_U}_DIR}") # user setting
751    endif ()
752    if (NOT DOXYGEN_${F}_DESTINATION)
753      if (DOXYGEN_DESTINATION)
754        set (DOXYGEN_${F}_DESTINATION "${DOXYGEN_DESTINATION}") # common destination
755      elseif (f MATCHES "man")
756        if (INSTALL_MAN_DIR)
757          set (DOXYGEN_MAN_DESTINATION "${INSTALL_MAN_DIR}/man${DOXYGEN_MAN_SECTION}") # default for manual pages
758        endif ()
759      elseif (NOT f MATCHES "html") # do not install excludes by default
760        set (DOXYGEN_${F}_DESTINATION "${INSTALL_DOC_DIR}") # default destination
761      endif ()
762    endif ()
763  endforeach ()
764  # determine tool to generate pdf documentation, see USE_PDFLATEX in Doxyfile.in
765  find_package (LATEX QUIET)
766  if (PDFLATEX_COMPILER)
767    set (DOXYGEN_USE_PDFLATEX YES)
768  else ()
769    set (DOXYGEN_USE_PDFLATEX NO)
770    message (STATUS "pdflatex not found. For higher quality PDFs make sure pdflatex is installed and found on the PATH.")
771  endif ()
772  # use default custom HTML files if available and none explicitly specified
773  if (NOT DOXYGEN_HTML_EXTRA_STYLESHEET)
774    if (EXISTS "${PROJECT_DOCRES_DIR}/doxygen_extra.css.in")
775      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOCRES_DIR}/doxygen_extra.css.in")
776    elseif (EXISTS "${PROJECT_DOCRES_DIR}/doxygen_extra.css")
777      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOCRES_DIR}/doxygen_extra.css")
778    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen_extra.css.in")
779      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOC_DIR}/doxygen_extra.css.in")
780    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen_extra.css")
781      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOC_DIR}/doxygen_extra.css")
782    elseif (EXISTS "${PROJECT_DOCRES_DIR}/doxygen.css.in")
783      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOCRES_DIR}/doxygen.css.in")
784    elseif (EXISTS "${PROJECT_DOCRES_DIR}/doxygen.css")
785      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOCRES_DIR}/doxygen.css")
786    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen.css.in")
787      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOC_DIR}/doxygen.css.in")
788    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen.css")
789      set (DOXYGEN_HTML_EXTRA_STYLESHEET "${PROJECT_DOC_DIR}/doxygen.css")
790    endif ()
791  endif ()
792  if (DOXYGEN_HTML_HEADER_IS_DEFAULT AND NOT DOXYGEN_HTML_EXTRA_STYLESHEET)
793    set (DOXYGEN_HTML_EXTRA_STYLESHEET "${BASIS_MODULE_PATH}/doxygen_extra.css.in")
794  endif ()
795  if (NOT DOXYGEN_HTML_HEADER)
796    if (EXISTS "${PROJECT_DOCRES_DIR}/doxygen_header.html.in")
797      set (DOXYGEN_HTML_HEADER "${PROJECT_DOCRES_DIR}/doxygen_header.html.in")
798    elseif (EXISTS "${PROJECT_DOCRES_DIR}/doxygen_header.html")
799      set (DOXYGEN_HTML_HEADER "${PROJECT_DOCRES_DIR}/doxygen_header.html")
800    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen_header.html.in")
801      set (DOXYGEN_HTML_HEADER "${PROJECT_DOC_DIR}/doxygen_header.html.in")
802    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen_header.html")
803      set (DOXYGEN_HTML_HEADER "${PROJECT_DOC_DIR}/doxygen_header.html")
804    endif ()
805  endif ()
806  if (NOT DOXYGEN_HTML_FOOTER)
807    if (EXISTS "${PROJECT_DOCRES_DIR}/doxygen_footer.html.in")
808      set (DOXYGEN_HTML_FOOTER "${PROJECT_DOCRES_DIR}/doxygen_footer.html.in")
809    elseif (EXISTS "${PROJECT_DOCRES_DIR}/doxygen_footer.html")
810      set (DOXYGEN_HTML_FOOTER "${PROJECT_DOCRES_DIR}/doxygen_footer.html")
811    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen_footer.html.in")
812      set (DOXYGEN_HTML_FOOTER "${PROJECT_DOC_DIR}/doxygen_footer.html.in")
813    elseif (EXISTS "${PROJECT_DOC_DIR}/doxygen_footer.html")
814      set (DOXYGEN_HTML_FOOTER "${PROJECT_DOC_DIR}/doxygen_footer.html")
815    endif ()
816  endif ()
817  # configure/copy custom HTML ressource files
818  if (DOXYGEN_GENERATE_HTML)
819    set (DOXYGEN_HTML_EXTRA_STYLESHEET_NAME "doxygen_extra.css")
820    set (OUTPUT_HTML_EXTRA_STYLESHEET "${DOXYGEN_OUTPUT_DIRECTORY}/${DOXYGEN_HTML_EXTRA_STYLESHEET_NAME}")
821    set (OUTPUT_HTML_HEADER           "${DOXYGEN_OUTPUT_DIRECTORY}/doxygen_header.html")
822    set (OUTPUT_HTML_FOOTER           "${DOXYGEN_OUTPUT_DIRECTORY}/doxygen_footer.html")
823    foreach (res IN ITEMS EXTRA_STYLESHEET HEADER FOOTER)
824      if (EXISTS "${DOXYGEN_HTML_${res}}")
825        if (DOXYGEN_HTML_${res} MATCHES "\\.in$")
826          configure_file (${DOXYGEN_HTML_${res}} "${OUTPUT_HTML_${res}}" @ONLY)
827        elseif (DOXYGEN_HTML_${res})
828          configure_file (${DOXYGEN_HTML_${res}} "${OUTPUT_HTML_${res}}" COPYONLY)
829        else ()
830          set (OUTPUT_HTML_${res})
831        endif ()
832      endif ()
833    endforeach ()
834    set  (DOXYGEN_HTML_EXTRA_STYLESHEET "${OUTPUT_HTML_EXTRA_STYLESHEET}")
835    set  (DOXYGEN_HTML_HEADER           "${OUTPUT_HTML_HEADER}")
836    set  (DOXYGEN_HTML_FOOTER           "${OUTPUT_HTML_FOOTER}")
837  else ()
838    set (DOXYGEN_HTML_EXTRA_STYLESHEET)
839    set (DOXYGEN_HTML_FOOTER)
840    set (DOXYGEN_HTML_HEADER)
841  endif ()
842  if (DOXYGEN_PROVIDER_LOGO)
843    list (APPEND DOXYGEN_HTML_EXTRA_FILES "${DOXYGEN_PROVIDER_LOGO}")
844  endif()
845  if (DOXYGEN_DIVISION_LOGO)
846    list (APPEND DOXYGEN_HTML_EXTRA_FILES "${DOXYGEN_DIVISION_LOGO}")
847  endif()
848  if (DOXYGEN_HTML_EXTRA_FILES)
849    basis_list_to_delimited_string (
850      DOXYGEN_HTML_EXTRA_FILES "\"\nHTML_EXTRA_FILES      += \"" ${DOXYGEN_HTML_EXTRA_FILES}
851    )
852  endif ()
853  # list of enabled Doxygen comment sections
854  basis_join ("${DOXYGEN_ENABLED_SECTIONS}" " " DOXYGEN_ENABLED_SECTIONS)
855  # configure Doxygen configuration file
856  set (DOXYFILE "${DOXYGEN_OUTPUT_DIRECTORY}/Doxyfile.${TARGET_NAME_L}")
857  configure_file ("${DOXYGEN_DOXYFILE}" "${DOXYFILE}" @ONLY)
858  if (CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
859    file (READ "${DOXYFILE}" DOXYFILE_CONTENT)
860    foreach (CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
861      string (REPLACE "$<${BASIS_GE_CONFIG}>" "${CONFIG}" DOXYFILE_CONTENT_CONFIG "${DOXYFILE_CONTENT}")
862      file (WRITE "${DOXYFILE}.${CONFIG}" "${DOXYFILE_CONTENT_CONFIG}")
863    endforeach ()
864    unset (DOXYFILE_CONTENT)
865    unset (DOXYFILE_CONTENT_CONFIG)
866    set (DOXYFILE "${DOXYFILE}.$<${BASIS_GE_CONFIG}>")
867  endif ()
868  # add build target
869  set (OPTALL)
870  if (BUILD_DOCUMENTATION AND BASIS_ALL_DOC)
871    set (OPTALL "ALL")
872  endif ()
873  file (MAKE_DIRECTORY "${DOXYGEN_OUTPUT_DIRECTORY}")
874  add_custom_target (
875    ${TARGET_UID} ${OPTALL} "${DOXYGEN_EXECUTABLE}" "${DOXYFILE}"
876    WORKING_DIRECTORY "${DOXYGEN_OUTPUT_DIRECTORY}"
877    COMMENT "Building documentation ${TARGET_UID}..."
878  )
879  # memorize certain settings which might be useful to know by other functions
880  # in particular, in case of the use of the XML output by other documentation
881  # build tools such as Sphinx, the function that wants to make use of this
882  # output can check if the Doxygen target has been configured properly and
883  # further requires to know the location of the XML output
884  set_target_properties (
885    ${TARGET_UID}
886    PROPERTIES
887      BASIS_TYPE       Doxygen
888      OUTPUT_DIRECTORY "${DOXYGEN_OUTPUT_DIRECTORY}"
889      DOXYFILE         "${DOXYGEN_DOXYFILE}"
890      TAGFILE          "${DOXYGEN_TAGFILE}"
891      OUTPUT           "${DOXYGEN_OUTPUT}"
892  )
893  foreach (f IN LISTS DOXYGEN_OUTPUT)
894    string (TOUPPER "${f}" F)
895    set_target_properties (
896      ${TARGET_UID}
897      PROPERTIES
898        ${F}_INSTALL_DIRECTORY "${DOXYGEN_${F}_DESTINATION}"
899        ${F}_OUTPUT_DIRECTORY  "${DOXYGEN_OUTPUT_DIRECTORY}/${DOXYGEN_${F}_OUTPUT}"
900    )
901    set_property (
902      DIRECTORY
903      APPEND PROPERTY
904        ADDITIONAL_MAKE_CLEAN_FILES
905          "${DOXYGEN_OUTPUT_DIRECTORY}/${DOXYGEN_${F}_OUTPUT}"
906    )
907  endforeach ()
908  if (DOXYGEN_TAGFILE)
909    set_property (
910      DIRECTORY
911      APPEND PROPERTY
912        ADDITIONAL_MAKE_CLEAN_FILES
913          "${DOXYGEN_TAGFILE}"
914    )
915  endif ()
916  # The Doxygen filter, if a build target of this project, has to be build
917  # before the documentation can be generated.
918  if (TARGET "${DOXYFILTER}")
919    add_dependencies (${TARGET_UID} ${DOXYFILTER})
920  endif ()
921  # The public header files shall be configured/copied before.
922  if (TARGET headers)
923    add_dependencies (${TARGET_UID} headers)
924  endif ()
925  # The documentation shall be build after all other executable and library
926  # targets have been build. For example, a .py.in script file shall first
927  # be "build", i.e., configured before the documentation is being generated
928  # from the configured .py file.
929  basis_get_project_property (TARGETS PROPERTY TARGETS)
930  foreach (_UID ${TARGETS})
931    get_target_property (BASIS_TYPE ${_UID} "BASIS_TYPE")
932    if (BASIS_TYPE MATCHES "SCRIPT|EXECUTABLE|LIBRARY")
933      add_dependencies (${TARGET_UID} ${_UID})
934    endif ()
935  endforeach ()
936  # add general "doc" target
937  if (NOT DOXYGEN_EXCLUDE_FROM_DOC)
938    if (NOT TARGET doc)
939      add_custom_target (doc)
940    endif ()
941    add_dependencies (doc ${TARGET_UID})
942  endif ()
943  # install documentation
944  install (
945    CODE
946      "
947      set (HTML_DESTINATION \"${DOXYGEN_HTML_DESTINATION}\")
948      set (MAN_DESTINATION  \"${DOXYGEN_MAN_DESTINATION}\")
949
950      function (install_doxydoc FMT)
951        string (TOUPPER \"\${FMT}\" FMT_U)
952        set (CMAKE_INSTALL_PREFIX \"\${\${FMT_U}_DESTINATION}\")
953        if (NOT CMAKE_INSTALL_PREFIX)
954          return ()
955        elseif (NOT IS_ABSOLUTE \"\${CMAKE_INSTALL_PREFIX}\")
956          set (CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}/\${CMAKE_INSTALL_PREFIX}\")
957        endif ()
958        set (EXT)
959        set (DIR \"\${FMT}\")
960        if (FMT MATCHES \".pdf\")
961          set (EXT \".pdf\")
962          set (DIR \"latex\")
963        elseif (FMT MATCHES \".rtf\")
964          set (EXT \".rtf\")
965        elseif (FMT MATCHES \"man\")
966          set (EXT \".?\")
967        endif ()
968        file (
969          GLOB_RECURSE
970            FILES
971          RELATIVE \"${DOXYGEN_OUTPUT_DIRECTORY}/\${DIR}\"
972            \"${DOXYGEN_OUTPUT_DIRECTORY}/\${DIR}/*\${EXT}\"
973        )
974        foreach (F IN LISTS FILES)
975          execute_process (
976            COMMAND \"${CMAKE_COMMAND}\" -E compare_files
977                \"${DOXYGEN_OUTPUT_DIRECTORY}/\${DIR}/\${F}\"
978                \"\${CMAKE_INSTALL_PREFIX}/\${F}\"
979            RESULT_VARIABLE RC
980            OUTPUT_QUIET
981            ERROR_QUIET
982          )
983          if (RC EQUAL 0)
984            message (STATUS \"Up-to-date: \${CMAKE_INSTALL_PREFIX}/\${F}\")
985          else ()
986            message (STATUS \"Installing: \${CMAKE_INSTALL_PREFIX}/\${F}\")
987            execute_process (
988              COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different
989                  \"${DOXYGEN_OUTPUT_DIRECTORY}/\${DIR}/\${F}\"
990                  \"\${CMAKE_INSTALL_PREFIX}/\${F}\"
991              RESULT_VARIABLE RC
992              OUTPUT_QUIET
993              ERROR_QUIET
994            )
995            if (RC EQUAL 0)
996              list (APPEND CMAKE_INSTALL_MANIFEST_FILES \"\${CMAKE_INSTALL_PREFIX}/\${F}\")
997            else ()
998              message (STATUS \"Failed to install \${CMAKE_INSTALL_PREFIX}/\${F}\")
999            endif ()
1000          endif ()
1001        endforeach ()
1002        if (FMT MATCHES \"html\" AND EXISTS \"${DOXYGEN_TAGFILE}\")
1003          get_filename_component (DOXYGEN_TAGFILE_NAME \"${DOXYGEN_TAGFILE}\" NAME)
1004          execute_process (
1005            COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different
1006              \"${DOXYGEN_TAGFILE}\"
1007              \"\${CMAKE_INSTALL_PREFIX}/\${DOXYGEN_TAGFILE_NAME}\"
1008          )
1009          list (APPEND CMAKE_INSTALL_MANIFEST_FILES \"\${CMAKE_INSTALL_PREFIX}/\${DOXYGEN_TAGFILE_NAME}\")
1010        endif ()
1011      endfunction ()
1012
1013      foreach (FMT IN ITEMS html pdf rtf man)
1014        install_doxydoc (\${FMT})
1015      endforeach ()
1016      "
1017  )
1018  # done
1019  message (STATUS "Adding documentation ${TARGET_UID}... - done")
1020endfunction ()
1021
1022# ----------------------------------------------------------------------------
1023## @brief Add documentation target to be generated by Sphinx (sphinx-build).
1024#
1025# This function adds a build target to generate documentation from
1026# <a href="http://docutils.sourceforge.net/rst.html">reStructuredText</a>
1027# (.rst files) using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
1028#
1029# @param [in] TARGET_NAME Name of the documentation target.
1030# @param [in] ARGN        List of arguments. The valid arguments are:
1031# @par
1032# <table border="0">
1033#   <tr>
1034#     @tp @b EXCLUDE_FROM_DOC @endtp
1035#     <td>By default, the specified target is build as part of the global
1036#         @c doc target. If this option is given, however, the added
1037#         documentation will not be build as part of this target.</td>
1038#   </tr>
1039#   <tr>
1040#     @tp @b BUILDER(S) builder... @endtp
1041#     <td>Sphinx builders to use. For each named builder, a build target
1042#         named &lt;TARGET_NAME&gt;_&lt;builder&gt; is added.</td>
1043#   </tr>
1044#   <tr>
1045#     @tp @b DEFAULT_BUILDER builder @endtp
1046#     <td>Default Sphinx builder to associated with the @c TARGET_NAME
1047#         build target. Defaults to the first builder named by @c BUILDERS.</td>
1048#   </tr>
1049#   <tr>
1050#     @tp @b AUTHOR(S) name @endtp
1051#     <td>Names of authors who wrote this documentation.
1052#         (default: @c PROJECT_AUTHORS)</td>
1053#   </tr>
1054#   <tr>
1055#     @tp @b COPYRIGHT text @endtp
1056#     <td>Copyright statement for generated files. (default: @c PROJECT_COPYRIGHT)</td>
1057#   </tr>
1058#   <tr>
1059#     @tp @b COMPONENT component @endtp
1060#     <td>Name of the component this documentation belongs to.
1061#         Defaults to @c BASIS_RUNTIME_COMPONENT.</td>
1062#   </tr>
1063#   <tr>
1064#     @tp @b DESTINATION dir @endtp
1065#     <td>Installation directory prefix. Used whenever there is no specific
1066#         destination specified for a particular Sphinx builder. Defaults to
1067#         @c BASIS_INSTALL_&ltTARGET&gt;_DIR in case of HTML output if set.
1068#         Otherwise, the generated HTML files are not installed.</td>
1069#   </tr>
1070#   <tr>
1071#     @tp @b &lt;BUILDER&gt;_DESTINATION dir @endtp
1072#     <td>Installation directory for files generated by the specific builder.<td>
1073#   </tr>
1074#   <tr>
1075#     @tp @b EXTENSIONS ext... @endtp
1076#     <td>Names of Sphinx extensions to enable.</td>
1077#   </tr>
1078#   <tr>
1079#     @tp @b BREATHE target... @endtp
1080#     <td>Adds a project for the breathe extension which allows the
1081#         inclusion of in-source code documentation extracted by Doxygen.
1082#         For this to work, the specified Doxygen target has to be
1083#         configured with the XML output enabled.</td>
1084#   </tr>
1085#   <tr>
1086#     @tp @b DOXYLINK target... @endtp
1087#     <td>Adds a role for the doxylink Sphinx extension which allows to cross-reference
1088#         generated HTML API documentation generated by Doxygen.</td>
1089#   </tr>
1090#   <tr>
1091#     @tp @b DOXYLINK_URL url @endtp
1092#     <td>URL to Doxygen documentation. Use DOXYLINK_PREFIX and/or DOXYLINK_SUFFIX
1093#         instead if you use multiple Doxygen targets, where the target name is
1094#         part of the URL.</td>
1095#   </tr>
1096#   <tr>
1097#     @tp @b DOXYLINK_PREFIX url @endtp
1098#     <td>Prefix to use for links to Doxygen generated documentation pages
1099#         as generated by the doxylink Sphinx extension. If this prefix does
1100#         not start with a protocol such as http:// or https://, it is prefixed
1101#         to the default path determined by this function relative to the build
1102#         or installed Doxygen documentation.</td>
1103#   </tr>
1104#   <tr>
1105#     @tp @b DOXYLINK_SUFFIX suffix @endtp
1106#     <td>Suffix for links to Doxygen generated documentation pages as generated
1107#         by the doxylink Sphinx extension.</td>
1108#   </tr>
1109#   <tr>
1110#     @tp @b DOXYDOC target... @endtp
1111#     <td>Alias for both @c BREATHE and @c DOXYLINK options.</td>
1112#   </tr>
1113#   <tr>
1114#     @tp @b CONFIG_FILE file @endtp
1115#     <td>Sphinx configuration file. Defaults to @c BASIS_SPHINX_CONFIG.</td>
1116#   </tr>
1117#   <tr>
1118#     @tp @b SOURCE_DIRECTORY @endtp
1119#     <td>Root directory of Sphinx source files.
1120#         Defaults to the current source directory or, if a subdirectory
1121#         named @c TARGET_NAME in lowercase only exists, to this subdirectory.</td>
1122#   </tr>
1123#   <tr>
1124#     @tp @b OUTPUT_NAME @endtp
1125#     <td>Output name for generated documentation such as PDF document or MAN page.
1126#         Defaults to @c PROJECT_NAME.</td>
1127#   </tr>
1128#   <tr>
1129#     @tp @b OUTPUT_DIRECTORY @endtp
1130#     <td>Root output directory for generated files. Defaults to the binary
1131#         directory corresponding to the set @c SOURCE_DIRECTORY.</td>
1132#   </tr>
1133#   <tr>
1134#     @tp @b TAG tag @endtp
1135#     <td>Tag argument of <tt>sphinx-build</tt>.</td>
1136#   </tr>
1137#   <tr>
1138#     @tp @b TEMPLATES_PATH @endtp
1139#     <td>Path to template files. Defaults to <tt>SOURCE_DIRECTORY/templates/</tt>.</td>
1140#   </tr>
1141#   <tr>
1142#     @tp @b MASTER_DOC name @endtp
1143#     <td>Name of master document. Defaults to <tt>index</tt>.</td>
1144#   </tr>
1145#   <tr>
1146#     @tp @b EXCLUDE_PATTERN pattern @endtp
1147#     <td>A glob-style pattern that should be excluded when looking for source files.
1148#         Specify this option more than once to specify multiple exclude patterns.
1149#         They are matched against the source file names relative to the source directory,
1150#         using slashes as directory separators on all platforms.</td>
1151#   </tr>
1152#   <tr>
1153#     @tp @b HTML_TITLE title @endtp
1154#     <td>Title of HTML web site.</td>
1155#   </tr>
1156#   <tr>
1157#     @tp @b HTML_THEME theme @endtp
1158#     <td>Name of HTML theme. Defaults to the @c sbia theme included with BASIS.</td>
1159#   </tr>
1160#   <tr>
1161#     @tp @b HTML_THEME_PATH dir @endtp
1162#     <td>Directory of HTML theme. Defaults to @c BASIS_SPHINX_HTML_THEME_PATH.</td>
1163#   </tr>
1164#   <tr>
1165#     @tp @b HTML_LOGO file @endtp
1166#     <td>Logo to display in sidebar of HTML pages.</td>
1167#   </tr>
1168#   <tr>
1169#     @tp @b HTML_FAVICON file @endtp
1170#     <td>Favorite square icon often displayed by browsers in the tab bar.
1171#         Should be a @c .ico file.</td>
1172#   </tr>
1173#   <tr>
1174#     @tp @b HTML_STATIC_PATH dir @endtp
1175#     <td>Directory for static files of HTML pages. Defaults to <tt>SOURCE_DIRECTORY/static/</tt>.</td>
1176#   </tr>
1177#   <tr>
1178#     @tp @b HTML_STYLE css @endtp
1179#     <td>The style sheet to use for HTML pages. A file of that name must exist either in Sphinx'
1180#         default static/ path or the specified @c HTML_STATIC_PATH. Default is the stylesheet
1181#         given by the selected theme. If you only want to add or override a few things compared
1182#         to the theme’s stylesheet, use CSS \@import to import the theme’s stylesheet.</td>
1183#   </tr>
1184#   <tr>
1185#     @tp @b HTML_SIDEBARS name... @endtp
1186#     <td>Names of HTML template files for sidebar(s). Defaults to none if not specified.
1187#         Valid default templates are @c localtoc, @c globaltoc, @c searchbox, @c relations,
1188#         @c sourcelink. See <a href="http://sphinx.pocoo.org/config.html#confval-html_sidebars">
1189#         Shinx documentation of html_sidebars option</a>. Custom templates can be used as
1190#         well by copying the template <tt>.html</tt> file to the @c TEMPLATES_PATH directory.</td>
1191#   </tr>
1192#   <tr>
1193#     @tp @b NO_HTML_DOMAIN_INDICES @endtp
1194#     <td>Set Sphinx configuration option @c html_domain_indices to @c False. (Default: @c True)</td>
1195#   </tr>
1196#   <tr>
1197#     @tp @b NO_HTML_MODINDEX @endtp
1198#     <td>Set Sphinx configuration option @c html_use_modindex to @c False. (Default: @c True)</td>
1199#   </tr>
1200#   <tr>
1201#     @tp @b NO_HTML_INDEX @endtp
1202#     <td>Set Sphinx configuration option @c html_use_index to @c False. (Default: @c True)</td>
1203#   </tr>
1204#   <tr>
1205#     @tp @b LATEX_MASTER_DOC name @endtp
1206#     <td>Name of master document for LaTeX builder. Defaults to <tt>MASTER_DOC</tt>.</td>
1207#   </tr>
1208#   <tr>
1209#     @tp @b LATEX_TITLE title @endtp
1210#     <td>Title for LaTeX/PDF output. Defaults to title of <tt>index.rst</tt>.</td>
1211#   </tr>
1212#   <tr>
1213#     @tp @b LATEX_LOGO file @endtp
1214#     <td>Logo to display above title in generated LaTeX/PDF output.</td>
1215#   </tr>
1216#   <tr>
1217#     @tp @b LATEX_DOCUMENT_CLASS howto|manual @endtp
1218#     <td>Document class to use by @c latex builder.</td>
1219#   </tr>
1220#   <tr>
1221#     @tp @b LATEX_SHOW_URLS @endtp
1222#     <td>See Sphinx documentation of the
1223#         <a href="http://sphinx.pocoo.org/config.html#confval-latex_show_urls">latex_show_urls</a> option.</td>
1224#   </tr>
1225#   <tr>
1226#     @tp @b LATEX_SHOW_PAGEREFS @endtp
1227#     <td>See Sphinx documentation of the
1228#         <a href="">latex_show_pagerefs</a> option.</td>
1229#   </tr>
1230#   <tr>
1231#     @tp @b MAN_SECTION num @endtp
1232#     <td>Section number for manual pages generated by @c man builder.</td>
1233#   </tr>
1234# </table>
1235#
1236# @sa basis_add_doc()
1237function (basis_add_sphinx_doc TARGET_NAME)
1238  # check target name
1239  basis_check_target_name ("${TARGET_NAME}")
1240  basis_make_target_uid (TARGET_UID "${TARGET_NAME}")
1241  string (TOLOWER "${TARGET_NAME}" TARGET_NAME_L)
1242  string (TOUPPER "${TARGET_NAME}" TARGET_NAME_U)
1243  # verbose output
1244  message (STATUS "Adding documentation ${TARGET_UID}...")
1245  # parse arguments
1246  set (ONE_ARG_OPTIONS
1247    COMPONENT
1248    DEFAUL_BUILDER
1249    DESTINATION HTML_DESTINATION MAN_DESTINATION TEXINFO_DESTINATION
1250    CONFIG_FILE
1251    SOURCE_DIRECTORY OUTPUT_DIRECTORY OUTPUT_NAME TAG
1252    COPYRIGHT MASTER_DOC
1253    HTML_TITLE HTML_THEME HTML_LOGO HTML_FAVICON HTML_THEME_PATH HTML_STYLE
1254    LATEX_MASTER_DOC LATEX_TITLE LATEX_LOGO LATEX_DOCUMENT_CLASS LATEX_SHOW_URLS LATEX_SHOW_PAGEREFS
1255    MAN_SECTION
1256    DOXYLINK_URL DOXYLINK_PREFIX DOXYLINK_SUFFIX
1257  )
1258  # note that additional multiple value arguments are parsed later on below
1259  # this is necessary b/c all unparsed arguments are considered to be options
1260  # of the used HTML theme
1261  CMAKE_PARSE_ARGUMENTS (SPHINX
1262    "EXCLUDE_FROM_DOC;NO_HTML_DOMAIN_INDICES;NO_HTML_MODINDEX;NO_HTML_INDEX"
1263    "${ONE_ARG_OPTIONS}"
1264    ""
1265    ${ARGN}
1266  )
1267  # source directory
1268  if (NOT SPHINX_SOURCE_DIRECTORY)
1269    if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}")
1270      set (SPHINX_SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME}")
1271    else ()
1272      set (SPHINX_SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
1273    endif ()
1274  elseif (NOT IS_ABSOLUTE "${SPHINX_SOURCE_DIRECTORY}")
1275    get_filename_component (SPHINX_SOURCE_DIRECTORY "${SPHINX_SOURCE_DIRECTORY}" ABSOLUTE)
1276  endif ()
1277  # component
1278  if (NOT SPHINX_COMPONENT)
1279    set (SPHINX_COMPONENT "${BASIS_RUNTIME_COMPONENT}")
1280  endif ()
1281  if (NOT SPHINX_COMPONENT)
1282    set (SPHINX_COMPONENT "Unspecified")
1283  endif ()
1284  # find Sphinx
1285  find_package (Sphinx COMPONENTS build QUIET)
1286  if (NOT Sphinx-build_EXECUTABLE)
1287    if (BUILD_DOCUMENTATION)
1288      message (FATAL_ERROR "Command sphinx-build not found! Either install Sphinx and/or set Sphinx-build_EXECUTABLE or disable BUILD_DOCUMENTATION.")
1289    endif ()
1290    message (STATUS "Command sphinx-build not found. Generation of ${TARGET_UID} documentation disabled.")
1291    message (STATUS "Adding documentation ${TARGET_UID}... - skipped")
1292    return ()
1293  endif ()
1294  if (DEFINED Sphinx_VERSION_MAJOR AND Sphinx_VERSION_MAJOR LESS 1)
1295    if (BUILD_DOCUMENTATION)
1296      message (FATAL_ERROR "Found sphinx-build is too old (v${Sphinx_VERSION_STRING})! Please install a more recent version and/or set Sphinx-build_EXECUTABLE to a newer version or disable BUILD_DOCUMENTATION.")
1297    endif ()
1298    message (STATUS "Command sphinx-build is too old (v${Sphinx_VERSION_STRING}). Generation of ${TARGET_UID} documentation disabled.")
1299    message (STATUS "Adding documentation ${TARGET_UID}... - skipped")
1300    return ()
1301  endif ()
1302  # parse remaining arguments
1303  set (SPHINX_HTML_THEME_OPTIONS)
1304  set (SPHINX_BUILDERS)
1305  set (SPHINX_AUTHORS)
1306  set (SPHINX_EXTENSIONS)
1307  set (SPHINX_BREATHE_TARGETS)
1308  set (SPHINX_DOXYLINK_TARGETS)
1309  set (SPHINX_HTML_SIDEBARS)
1310  set (SPHINX_TEMPLATES_PATH)
1311  set (SPHINX_HTML_STATIC_PATH)
1312  set (SPHINX_EXCLUDE_PATTERNS)
1313  set (SPHINX_DEPENDS)
1314  set (OPTION_NAME)
1315  set (OPTION_VALUE)
1316  set (OPTION_PATTERN "(authors?|builders?|extensions|breathe|doxylink|doxydoc|html_sidebars|templates_path|html_static_path|exclude_pattern)")
1317  foreach (ARG IN LISTS SPHINX_UNPARSED_ARGUMENTS)
1318    if (NOT OPTION_NAME OR ARG MATCHES "^[A-Z_]+$")
1319      # SPHINX_HTML_THEME_OPTIONS
1320      if (OPTION_NAME AND NOT OPTION_NAME MATCHES "^${OPTION_PATTERN}$")
1321        if (NOT OPTION_VALUE)
1322          message (FATAL_ERROR "Option ${OPTION_NAME} is missing an argument!")
1323        endif ()
1324        list (LENGTH OPTION_VALUE NUM)
1325        if (NUM GREATER 1)
1326          basis_list_to_delimited_string (OPTION_VALUE ", " NOAUTOQUOTE ${OPTION_VALUE})
1327          set (OPTION_VALUE "[${OPTION_VALUE}]")
1328        endif ()
1329        list (APPEND SPHINX_HTML_THEME_OPTIONS "'${OPTION_NAME}': ${OPTION_VALUE}")
1330      endif ()
1331      # name of next option
1332      set (OPTION_NAME "${ARG}")
1333      set (OPTION_VALUE)
1334      string (TOLOWER "${OPTION_NAME}" OPTION_NAME)
1335    # BUILDER option
1336    elseif (OPTION_NAME MATCHES "^builders?$")
1337      if (ARG MATCHES "html dirhtml singlehtml pdf latex man text texinfo linkcheck")
1338        message (FATAL_ERROR "Invalid/Unsupported Sphinx builder: ${ARG}")
1339      endif ()
1340      list (APPEND SPHINX_BUILDERS "${ARG}")
1341    # AUTHORS option
1342    elseif (OPTION_NAME MATCHES "^authors?$")
1343      list (APPEND SPHINX_AUTHORS "'${ARG}'")
1344    # EXTENSIONS option
1345    elseif (OPTION_NAME MATCHES "^extensions$")
1346      # built-in extension
1347      if (ARG MATCHES "^(autodoc|autosummary|doctest|intersphinx|pngmath|jsmath|mathjax|graphvis|inheritance_graph|ifconfig|coverage|todo|extlinks|viewcode)$")
1348        set (ARG "sphinx.ext.${CMAKE_MATCH_0}")
1349      # map originial name of extensions included with BASIS
1350      elseif (BASIS_SPHINX_EXTENSIONS_PATH AND ARG MATCHES "^sphinxcontrib.(doxylink)$")
1351        set (ARG "${CMAKE_MATCH_1}")
1352      endif ()
1353      list (APPEND SPHINX_EXTENSIONS "'${ARG}'")
1354    # DOXYDOC
1355    elseif (OPTION_NAME MATCHES "^doxydoc$")
1356      list (APPEND SPHINX_BREATHE_TARGETS  "${ARG}")
1357      list (APPEND SPHINX_DOXYLINK_TARGETS "${ARG}")
1358    # BREATHE
1359    elseif (OPTION_NAME MATCHES "^breathe$")
1360      list (APPEND SPHINX_BREATHE_TARGETS "${ARG}")
1361    # DOXYLINK
1362    elseif (OPTION_NAME MATCHES "^doxylink$")
1363      list (APPEND SPHINX_DOXYLINK_TARGETS "${ARG}")
1364    # HTML_SIDEBARS
1365    elseif (OPTION_NAME MATCHES "^html_sidebars$")
1366      if (NOT ARG MATCHES "\\.html?$")
1367        set (ARG "${ARG}.html")
1368      endif ()
1369      list (APPEND SPHINX_HTML_SIDEBARS "'${ARG}'")
1370    # TEMPLATES_PATH
1371    elseif (OPTION_NAME MATCHES "^templates_path$")
1372      if (NOT IS_ABSOLUTE "${ARG}")
1373        set (ARG "${SPHINX_SOURCE_DIRECTORY}/${ARG}")
1374      endif ()
1375      list (APPEND SPHINX_TEMPLATES_PATH "'${ARG}'")
1376    # HTML_STATIC_PATH
1377    elseif (OPTION_NAME MATCHES "^html_static_path$")
1378      if (NOT IS_ABSOLUTE "${ARG}")
1379        set (ARG "${SPHINX_SOURCE_DIRECTORY}/${ARG}")
1380      endif ()
1381      list (APPEND SPHINX_HTML_STATIC_PATH "'${ARG}'")
1382    # EXCLUDE_PATTERN
1383    elseif (OPTION_NAME MATCHES "^exclude_pattern$")
1384      list (APPEND SPHINX_EXCLUDE_PATTERNS "'${ARG}'")
1385    # value of theme option
1386    else ()
1387      if (ARG MATCHES "^(TRUE|FALSE)$")
1388        string (TOLOWER "${ARG}" "${ARG}")
1389      endif ()
1390      if (NOT ARG MATCHES "^\\[.*\\]$|^{.*}$")
1391        set (ARG "'${ARG}'")
1392      endif ()
1393      list (APPEND OPTION_VALUE "${ARG}")
1394    endif ()
1395  endforeach ()
1396  # append parsed option setting to SPHINX_HTML_THEME_OPTIONS
1397  if (OPTION_NAME AND NOT OPTION_NAME MATCHES "^${OPTION_PATTERN}$")
1398    if (NOT OPTION_VALUE)
1399      message (FATAL_ERROR "Option ${OPTION_NAME} is missing an argument!")
1400    endif ()
1401    list (LENGTH OPTION_VALUE NUM)
1402    if (NUM GREATER 1)
1403      basis_list_to_delimited_string (OPTION_VALUE ", " NOAUTOQUOTE ${OPTION_VALUE})
1404      set (OPTION_VALUE "[${OPTION_VALUE}]")
1405    endif ()
1406    list (APPEND SPHINX_HTML_THEME_OPTIONS "'${OPTION_NAME}': ${OPTION_VALUE}")
1407  endif ()
1408  # authors
1409  if (NOT SPHINX_AUTHORS)
1410    foreach (AUTHOR IN LISTS PROJECT_AUTHORS)
1411      list (APPEND SPHINX_AUTHORS "'${AUTHOR}'")
1412    endforeach ()
1413  endif ()
1414  if (NOT SPHINX_COPYRIGHT)
1415    set (SPHINX_COPYRIGHT "${PROJECT_COPYRIGHT}")
1416  endif ()
1417  # default builders
1418  if (NOT SPHINX_BUILDERS)
1419    set (SPHINX_BUILDERS html dirhtml singlehtml man pdf texinfo text linkcheck)
1420  endif ()
1421  if (SPHINX_DEFAULT_BUILDER)
1422    list (FIND SPHINX_BUILDERS "${SPHINX_DEFAULT_BUILDER}" IDX)
1423    if (IDX EQUAL -1)
1424      list (INSERT SPHINX_BUILDERS 0 "${SPHINX_DEFAULT_BUILDER}")
1425    endif ()
1426  else ()
1427    list (GET SPHINX_BUILDERS 0 SPHINX_DEFAULT_BUILDER)
1428  endif ()
1429  # output directories
1430  if (NOT SPHINX_OUTPUT_NAME)
1431    set (SPHINX_OUTPUT_NAME "${PROJECT_NAME}")
1432  endif ()
1433  if (NOT SPHINX_OUTPUT_DIRECTORY)
1434    if (IS_ABSOLUTE "${SPHINX_OUTPUT_NAME}")
1435      get_filename_component (SPHINX_OUTPUT_DIRECTORY "${SPHINX_OUTPUT_NAME}" PATH)
1436    else ()
1437      basis_get_relative_path (SPHINX_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" "${SPHINX_SOURCE_DIRECTORY}")
1438    endif ()
1439  endif ()
1440  if (NOT IS_ABSOLUTE "${SPHINX_OUTPUT_DIRECTORY}")
1441    set (SPHINX_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${SPHINX_OUTPUT_DIRECTORY}")
1442  endif ()
1443  foreach (b IN LISTS SPHINX_BUILDERS)
1444    string (TOUPPER "${b}" B)
1445    if (SPHINX_${B}_OUTPUT_DIRECTORY)
1446      if (NOT IS_ABSOLUTE "${SPHINX_${B}_OUTPUT_DIRECTORY}")
1447        set (SPHINX_${B}_OUTPUT_DIRECTORY "${SPHINX_OUTPUT_DIRECTORY}/${SPHINX_${B}_OUTPUT_DIRECTORY}")
1448      endif ()
1449    else ()
1450      set (SPHINX_${B}_OUTPUT_DIRECTORY "${SPHINX_OUTPUT_DIRECTORY}/${b}")
1451    endif ()
1452  endforeach ()
1453  if (IS_ABSOLUTE "${SPHINX_OUTPUT_NAME}")
1454    basis_get_relative_path (SPHINX_OUTPUT_NAME "${SPHINX_OUTPUT_DIRECTORY}" NAME_WE)
1455  endif ()
1456  # configuration directory
1457  basis_get_relative_path (SPHINX_CONFIG_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" "${SPHINX_SOURCE_DIRECTORY}")
1458  set (SPHINX_CONFIG_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${SPHINX_CONFIG_DIRECTORY}")
1459  # build configuration
1460  if (NOT SPHINX_MASTER_DOC)
1461    set (SPHINX_MASTER_DOC "index")
1462  endif ()
1463  if (NOT SPHINX_LATEX_MASTER_DOC)
1464    set (SPHINX_LATEX_MASTER_DOC "${SPHINX_MASTER_DOC}")
1465  endif ()
1466  if (NOT SPHINX_TEMPLATES_PATH AND EXISTS "${SPHINX_SOURCE_DIRECTORY}/templates")
1467    set (SPHINX_TEMPLATES_PATH "'${SPHINX_SOURCE_DIRECTORY}/templates'")
1468  endif ()
1469  if (NOT SPHINX_HTML_STATIC_PATH AND EXISTS "${SPHINX_SOURCE_DIRECTORY}/static")
1470    set (SPHINX_HTML_STATIC_PATH "'${SPHINX_SOURCE_DIRECTORY}/static'")
1471  endif ()
1472  if (NOT SPHINX_HTML_THEME)
1473    set (SPHINX_HTML_THEME "${BASIS_SPHINX_HTML_THEME}")
1474  endif ()
1475  if (NOT SPHINX_UNPARSED_ARGUMENTS AND SPHINX_HTML_THEME STREQUAL BASIS_SPHINX_HTML_THEME)
1476    set (SPHINX_UNPARSED_ARGUMENTS ${BASIS_SPHINX_HTML_THEME_OPTIONS})
1477  endif ()
1478  if (NOT SPHINX_LATEX_DOCUMENT_CLASS)
1479    set (SPHINX_LATEX_DOCUMENT_CLASS "howto")
1480  endif ()
1481  if (NOT SPHINX_MAN_SECTION)
1482    set (SPHINX_MAN_SECTION 1)
1483  endif ()
1484  # installation directories
1485  set (BASIS_INSTALL_${TARGET_NAME_U}_DIR "" CACHE PATH "Installation directory for documentation ${TARGET_NAME} target.")
1486  mark_as_advanced (BASIS_INSTALL_${TARGET_NAME_U}_DIR)
1487  foreach (b IN LISTS SPHINX_BUILDERS)
1488    string (TOUPPER "${b}" B)
1489    if (BASIS_INSTALL_${TARGET_NAME_U}_DIR)
1490      set (SPHINX_${B}_DESTINATION "${BASIS_INSTALL_${TARGET_NAME_U}_DIR}") # user setting
1491    endif ()
1492    if (NOT SPHINX_${B}_DESTINATION)
1493      if (SPHINX_DESTINATION)
1494        set (SPHINX_${B}_DESTINATION "${DESTINATION}") # common destination
1495      elseif (b MATCHES "text")
1496        set (SPHINX_${B}_DESTINATION "${INSTALL_DOC_DIR}/${TARGET_NAME_L}")
1497      elseif (b MATCHES "man")
1498        if (INSTALL_MAN_DIR)
1499          set (SPHINX_${B}_DESTINATION "${INSTALL_MAN_DIR}/man${SPHINX_MAN_SECTION}") # default for manual pages
1500        endif ()
1501      elseif (b MATCHES "texinfo")
1502        if (INSTALL_TEXINFO_DIR)
1503          set (SPHINX_${B}_DESTINATION "${INSTALL_TEXINFO_DIR}") # default for Texinfo files
1504        endif ()
1505      elseif (NOT b MATCHES "html") # do not install excludes by default
1506        set (SPHINX_${B}_DESTINATION "${INSTALL_DOC_DIR}") # default location
1507      endif ()
1508    endif ()
1509  endforeach ()
1510  if (SPHINX_HTML_DESTINATION)
1511    foreach (b IN LISTS SPHINX_BUILDERS)
1512      if (b MATCHES "(dir|single)html")
1513        string (TOUPPER "${b}" B)
1514        if (NOT SPHINX_${B}_DESTINATION)
1515          set (SPHINX_${B}_DESTINATION "${SPHINX_HTML_DESTINATION}")
1516        endif ()
1517      endif ()
1518    endforeach ()
1519  endif ()
1520  # enable required extension
1521  if (SPHINX_DOXYLINK_TARGETS)
1522    if (BASIS_SPHINX_EXTENSIONS_PATH)
1523      if (NOT SPHINX_EXTENSIONS MATCHES "(^|;)?doxylink(;|$)?")
1524        list (APPEND SPHINX_EXTENSIONS "'doxylink'")
1525      endif ()
1526    else ()
1527      if (NOT SPHINX_EXTENSIONS MATCHES "(^|;)?sphinxcontrib.doxylink(;|$)?")
1528        list (APPEND SPHINX_EXTENSIONS "'sphinxcontrib.doxylink'")
1529      endif ()
1530    endif ()
1531  endif ()
1532  if (SPHINX_BREATHE_TARGETS AND NOT SPHINX_EXTENSIONS MATCHES "(^|;)?breathe(;|$)?")
1533    list (APPEND SPHINX_EXTENSIONS "'breathe'")
1534  endif ()
1535  # doxylink configuration
1536  foreach (TARGET IN LISTS SPHINX_DOXYLINK_TARGETS)
1537    basis_get_target_uid (UID "${TARGET}")
1538    get_target_property (TYPE ${UID} BASIS_TYPE)
1539    if (NOT TYPE MATCHES "Doxygen")
1540      message (FATAL_ERROR "Invalid argument for DOXYLINK: Target ${UID} either unknown or it is not a Doxygen target!")
1541    endif ()
1542    get_target_property (DOXYGEN_OUTPUT ${UID} OUTPUT)
1543    if (NOT DOXYGEN_OUTPUT MATCHES "html")
1544      message (FATAL_ERROR "Doxygen target ${UID} was not configured to generate HTML output! This output is required by the doxylink Sphinx extension.")
1545    endif ()
1546    get_target_property (DOXYGEN_TAGFILE        ${UID} TAGFILE)
1547    get_target_property (DOXYGEN_HTML_DIRECTORY ${UID} HTML_INSTALL_DIRECTORY)
1548    set (DOXYLINK_PATH)
1549    if (SPHINX_DOXYLINK_URL)
1550      set (DOXYLINK_PATH "${SPHINX_DOXYLINK_URL}")
1551    elseif (SPHINX_DOXYLINK_PREFIX MATCHES "^[a-z]://")
1552      set (DOXYLINK_PATH "${SPHINX_DOXYLINK_PREFIX}${TARGET}${SPHINX_DOXYLINK_SUFFIX}")
1553    else ()
1554      set (DOXYLINK_BASE_DIR)
1555      if (DOXYGEN_HTML_DIRECTORY)
1556        if (SPHINX_DOXYLINK_BASE_DIR)
1557          set (DOXYLINK_BASE_DIR "${SPHINX_DOXYLINK_BASE_DIR}")
1558        elseif (SPHINX_HTML_INSTALL_DIRECTORY)
1559          set (DOXYLINK_BASE_DIR "${SPHINX_HTML_INSTALL_DIRECTORY}")
1560        endif ()
1561      else ()
1562        get_target_property (DOXYGEN_HTML_DIRECTORY ${UID} HTML_OUTPUT_DIRECTORY)
1563        if (SPHINX_DOXYLINK_BASE_DIR)
1564          set (DOXYLINK_BASE_DIR "${SPHINX_DOXYLINK_BASE_DIR}")
1565        else ()
1566          set (DOXYLINK_BASE_DIR "${SPHINX_HTML_OUTPUT_DIRECTORY}")
1567        endif ()
1568      endif ()
1569      if (DOXYLINK_BASE_DIR)
1570        basis_get_relative_path (DOXYLINK_PATH "${DOXYLINK_BASE_DIR}" "${DOXYGEN_HTML_DIRECTORY}")
1571      else ()
1572        set (DOXYLINK_PATH "${TARGET}") # safe fall back
1573      endif ()
1574      set (DOXYLINK_PATH "${SPHINX_DOXYLINK_PREFIX}${DOXYLINK_PATH}${SPHINX_DOXYLINK_SUFFIX}")
1575    endif ()
1576    list (APPEND SPHINX_DOXYLINK "'${TARGET}': ('${DOXYGEN_TAGFILE}', '${DOXYLINK_PATH}')")
1577    list (APPEND SPHINX_DEPENDS ${UID})
1578  endforeach ()
1579  # breathe configuration
1580  set (SPHINX_BREATHE_PROJECTS)
1581  set (SPHINX_BREATHE_DEFAULT_PROJECT)
1582  foreach (TARGET IN LISTS SPHINX_BREATHE_TARGETS)
1583    basis_get_target_uid (UID "${TARGET}")
1584    get_target_property (TYPE ${UID} BASIS_TYPE)
1585    if (NOT TYPE MATCHES "Doxygen")
1586      message (FATAL_ERROR "Invalid argument for BREATHE_PROJECTS: Target ${UID} either unknown or it is not a Doxygen target!")
1587    endif ()
1588    get_target_property (DOXYGEN_OUTPUT ${UID} OUTPUT)
1589    if (NOT DOXYGEN_OUTPUT MATCHES "xml")
1590      message (FATAL_ERROR "Doxygen target ${UID} was not configured to generate XML output! This output is required by the Sphinx extension breathe.")
1591    endif ()
1592    get_target_property (DOXYGEN_OUTPUT_DIRECTORY ${UID} XML_OUTPUT_DIRECTORY)
1593    list (APPEND SPHINX_BREATHE_PROJECTS "'${TARGET}': '${DOXYGEN_OUTPUT_DIRECTORY}'")
1594    if (NOT SPHINX_BREATHE_DEFAULT_PROJECT)
1595      set (SPHINX_BREATHE_DEFAULT_PROJECT "${TARGET}")
1596    endif ()
1597    list (APPEND SPHINX_DEPENDS ${UID})
1598  endforeach ()
1599  # HTML output options
1600  if (SPHINX_NO_HTML_DOMAIN_INDICES)
1601    set (SPHINX_USE_DOMAIN_INDICES False)
1602  else ()
1603    set (SPHINX_USE_DOMAIN_INDICES True)
1604  endif ()
1605  if (SPHINX_NO_HTML_MODINDEX)
1606    set (SPHINX_USE_MODINDEX False)
1607  else ()
1608    set (SPHINX_USE_MODINDEX True)
1609  endif ()
1610  if (SPHINX_NO_HTML_INDEX)
1611    set (SPHINX_USE_INDEX False)
1612  else ()
1613    set (SPHINX_USE_INDEX True)
1614  endif ()
1615  # LaTeX output options
1616  if (NOT SPHINX_LATEX_SHOW_URLS)
1617    set (SPHINX_LATEX_SHOW_URLS "no")
1618  endif ()
1619  if (SPHINX_LATEX_SHOW_PAGEREFS)
1620    set (SPHINX_LATEX_SHOW_PAGEREFS "True")
1621  else ()
1622    set (SPHINX_LATEX_SHOW_PAGEREFS "False")
1623  endif ()
1624  # turn html_logo, html_favicon, and latex_logo into absolute file path
1625  foreach (L IN ITEMS HTML_LOGO HTML_FAVICON LATEX_LOGO)
1626    if (SPHINX_${L} AND NOT IS_ABSOLUTE "${SPHINX_${L}}")
1627      if (EXISTS "${SPHINX_SOURCE_DIRECTORY}/${SPHINX_${L}}")
1628        set (SPHINX_${L} "${SPHINX_SOURCE_DIRECTORY}/${SPHINX_${L}}")
1629      elseif (L MATCHES "^HTML|^LATEX")
1630        foreach (D IN LISTS SPHINX_${CMAKE_MATCH_0}_STATIC_PATH)
1631          string (REGEX REPLACE "^'|'$" "" D "${D}")
1632          if (EXISTS "${D}/${SPHINX_${L}}")
1633            set (SPHINX_${L} "${D}/${SPHINX_${L}}")
1634            break ()
1635          endif ()
1636        endforeach ()
1637      endif ()
1638    endif ()
1639  endforeach ()
1640  # turn CMake lists into Python lists
1641  basis_list_to_delimited_string (SPHINX_EXTENSIONS         ", " NOAUTOQUOTE ${SPHINX_EXTENSIONS})
1642  basis_list_to_delimited_string (SPHINX_HTML_THEME_OPTIONS ", " NOAUTOQUOTE ${SPHINX_HTML_THEME_OPTIONS})
1643  basis_list_to_delimited_string (SPHINX_AUTHORS            ", " NOAUTOQUOTE ${SPHINX_AUTHORS})
1644  basis_list_to_delimited_string (SPHINX_DOXYLINK           ", " NOAUTOQUOTE ${SPHINX_DOXYLINK})
1645  basis_list_to_delimited_string (SPHINX_BREATHE_PROJECTS   ", " NOAUTOQUOTE ${SPHINX_BREATHE_PROJECTS})
1646  basis_list_to_delimited_string (SPHINX_HTML_SIDEBARS      ", " NOAUTOQUOTE ${SPHINX_HTML_SIDEBARS})
1647  basis_list_to_delimited_string (SPHINX_TEMPLATES_PATH     ", " NOAUTOQUOTE ${SPHINX_TEMPLATES_PATH})
1648  basis_list_to_delimited_string (SPHINX_HTML_STATIC_PATH   ", " NOAUTOQUOTE ${SPHINX_HTML_STATIC_PATH})
1649  basis_list_to_delimited_string (SPHINX_EXCLUDE_PATTERNS   ", " NOAUTOQUOTE ${SPHINX_EXCLUDE_PATTERNS})
1650  # configuration file
1651  if (NOT SPHINX_CONFIG_FILE)
1652    set (SPHINX_CONFIG_FILE "${BASIS_SPHINX_CONFIG}")
1653  endif ()
1654  get_filename_component (SPHINX_CONFIG_FILE "${SPHINX_CONFIG_FILE}" ABSOLUTE)
1655  if (EXISTS "${SPHINX_CONFIG_FILE}")
1656    configure_file ("${SPHINX_CONFIG_FILE}" "${SPHINX_CONFIG_DIRECTORY}/conf.py" @ONLY)
1657  elseif (EXISTS "${SPHINX_CONFIG_FILE}.in")
1658    configure_file ("${SPHINX_CONFIG_FILE}.in" "${SPHINX_CONFIG_DIRECTORY}/conf.py" @ONLY)
1659  else ()
1660    message (FATAL_ERROR "Missing Sphinx configuration file ${SPHINX_CONFIG_FILE}!")
1661  endif ()
1662  # add target to build documentation
1663  set (OPTIONS -a -N -n)
1664  if (NOT BASIS_VERBOSE)
1665    list (APPEND OPTIONS "-q")
1666  endif ()
1667  foreach (TAG IN LISTS SPHINX_TAG)
1668    list (APPEND OPTIONS "-t" "${TAG}")
1669  endforeach ()
1670  add_custom_target (${TARGET_UID}_all) # target to run all builders
1671  foreach (BUILDER IN LISTS SPHINX_BUILDERS)
1672    set (SPHINX_BUILDER "${BUILDER}")
1673    set (SPHINX_POST_COMMAND)
1674    if (BUILDER MATCHES "pdf|texinfo")
1675      if (UNIX)
1676        set (SPHINX_MAKE_COMMAND "make")
1677        if (BUILDER MATCHES "pdf")
1678          set (SPHINX_BUILDER "latex")
1679          configure_file ("${BASIS_MODULE_PATH}/sphinx_make.sh.in" "${SPHINX_CONFIG_DIRECTORY}/make.sh" @ONLY)
1680          set (SPHINX_MAKE_COMMAND "${SPHINX_CONFIG_DIRECTORY}/make.sh")
1681          execute_process (COMMAND chmod +x "${SPHINX_CONFIG_DIRECTORY}/make.sh")
1682        endif ()
1683        set (SPHINX_POST_COMMAND COMMAND "${SPHINX_MAKE_COMMAND}" -C "${SPHINX_OUTPUT_DIRECTORY}/${SPHINX_BUILDER}")
1684      elseif (BUILDER MATCHES "pdf")
1685        message (WARNING "Target ${TARGET_UID} requires the execution of pdflatex which is currently"
1686                         " only executed after sphinx-build on Unix platforms. On Windows, pdflatex"
1687                         " must be executed manually in the directory\n\n\t${SPHINX_OUTPUT_DIRECTORY}/${SPHINX_BUILDER}")
1688      endif ()
1689    endif ()
1690    if (Sphinx_PYTHON_EXECUTABLE)
1691      add_custom_target (
1692        ${TARGET_UID}_${BUILDER}
1693            "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS}
1694                "${Sphinx-build_EXECUTABLE}" ${OPTIONS}
1695                  -b ${SPHINX_BUILDER}
1696                  -c "${SPHINX_CONFIG_DIRECTORY}"
1697                  -d "${SPHINX_CONFIG_DIRECTORY}/doctrees"
1698                  "${SPHINX_SOURCE_DIRECTORY}"
1699                  "${SPHINX_OUTPUT_DIRECTORY}/${SPHINX_BUILDER}"
1700            ${SPHINX_POST_COMMAND}
1701            ${OPTDEPENDS}
1702        WORKING_DIRECTORY "${SPHINX_CONFIG_DIRECTORY}"
1703        COMMENT "Building documentation ${TARGET_UID} (${BUILDER})..."
1704      )
1705    elseif (UNIX)
1706      add_custom_target (
1707        ${TARGET_UID}_${BUILDER}
1708            "${Sphinx-build_EXECUTABLE}" ${OPTIONS}
1709                  -b ${SPHINX_BUILDER}
1710                  -c "${SPHINX_CONFIG_DIRECTORY}"
1711                  -d "${SPHINX_CONFIG_DIRECTORY}/doctrees"
1712                  "${SPHINX_SOURCE_DIRECTORY}"
1713                  "${SPHINX_OUTPUT_DIRECTORY}/${SPHINX_BUILDER}"
1714            ${SPHINX_POST_COMMAND}
1715            ${OPTDEPENDS}
1716        WORKING_DIRECTORY "${SPHINX_CONFIG_DIRECTORY}"
1717        COMMENT "Building documentation ${TARGET_UID} (${BUILDER})..."
1718      )
1719    else ()
1720      message (FATAL_ERROR "Python executable required to run Sphinx could not be determined by FindSphinx.cmake!"
1721                           " Set the advanced PYTHON_EXECUTABLE variable and try again.")
1722    endif ()
1723    if (SPHINX_DEPENDS)
1724      add_dependencies (${TARGET_UID}_${BUILDER} ${SPHINX_DEPENDS})
1725    endif ()
1726    add_dependencies (${TARGET_UID}_all ${TARGET_UID}_${BUILDER})
1727    # cleanup on "make clean"
1728    set_property (
1729      DIRECTORY
1730      APPEND PROPERTY
1731        ADDITIONAL_MAKE_CLEAN_FILES
1732          "${SPHINX_OUTPUT_DIRECTORY}"
1733    )
1734  endforeach ()
1735  # add general target which depends on default builder only
1736  if (BUILD_DOCUMENTATION AND BASIS_ALL_DOC)
1737    add_custom_target (${TARGET_UID} ALL)
1738  else ()
1739    add_custom_target (${TARGET_UID})
1740  endif ()
1741  add_dependencies (${TARGET_UID} ${TARGET_UID}_${SPHINX_DEFAULT_BUILDER})
1742  # add general "doc" target
1743  if (NOT SPHINX_EXCLUDE_FROM_DOC)
1744    if (NOT TARGET doc)
1745      add_custom_target (doc)
1746    endif ()
1747    add_dependencies (doc ${TARGET_UID}_${SPHINX_DEFAULT_BUILDER})
1748  endif ()
1749  # memorize important target properties
1750  set_target_properties (
1751    ${TARGET_UID}
1752    PROPERTIES
1753      BASIS_TYPE       Sphinx
1754      BUILDERS         "${SPHINX_BUILDERS}"
1755      SOURCE_DIRECTORY "${SPHINX_SOURCE_DIRECTORY}"
1756      OUTPUT_DIRECTORY "${SPHINX_OUTPUT_DIRECTORY}"
1757      CONFIG_DIRECTORY "${SPHINX_CONFIG_DIRECTORY}"
1758  )
1759  foreach (b IN LISTS SPHINX_BUILDERS)
1760    string (TOUPPER ${b} B)
1761    set_target_properties (${TARGET_UID} PROPERTIES ${B}_INSTALL_DIRECTORY "${SPHINX_${B}_DESTINATION}")
1762  endforeach ()
1763  # cleanup on "make clean"
1764  set_property (
1765    DIRECTORY
1766    APPEND PROPERTY
1767      ADDITIONAL_MAKE_CLEAN_FILES
1768        "${SPHINX_CONFIG_DIRECTORY}/doctrees"
1769  )
1770  # install documentation
1771  install (
1772    CODE
1773      "
1774      set (HTML_DESTINATION    \"${SPHINX_HTML_DESTINATION}\")
1775      set (PDF_DESTINATION     \"${SPHINX_PDF_DESTINATION}\")
1776      set (LATEX_DESTINATION   \"${SPHINX_LATEX_DESTINATION}\")
1777      set (MAN_DESTINATION     \"${SPHINX_MAN_DESTINATION}\")
1778      set (TEXINFO_DESTINATION \"${SPHINX_TEXINFO_DESTINATION}\")
1779      set (TEXT_DESTINATION    \"${SPHINX_TEXT_DESTINATION}\")
1780
1781      function (install_sphinx_doc BUILDER)
1782        if (BUILDER MATCHES \"pdf\")
1783          set (SPHINX_BUILDER \"latex\")
1784        else ()
1785          set (SPHINX_BUILDER \"\${BUILDER}\")
1786        endif ()
1787        string (TOUPPER \"\${BUILDER}\" BUILDER_U)
1788        set (CMAKE_INSTALL_PREFIX \"\${\${BUILDER_U}_DESTINATION}\")
1789        if (NOT CMAKE_INSTALL_PREFIX)
1790          return ()
1791        elseif (NOT IS_ABSOLUTE \"\${CMAKE_INSTALL_PREFIX}\")
1792          set (CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}/\${CMAKE_INSTALL_PREFIX}\")
1793        endif ()
1794        set (EXT)
1795        if (BUILDER MATCHES \"pdf\")
1796          set (EXT \".pdf\")
1797        elseif (BUILDER MATCHES \"man\")
1798          set (EXT \".?\")
1799        elseif (BUILDER MATCHES \"texinfo\")
1800          set (EXT \".info\")
1801        endif ()
1802        file (
1803          GLOB_RECURSE
1804            FILES
1805          RELATIVE \"${SPHINX_OUTPUT_DIRECTORY}/\${SPHINX_BUILDER}\"
1806            \"${SPHINX_OUTPUT_DIRECTORY}/\${SPHINX_BUILDER}/*\${EXT}\"
1807        )
1808        foreach (F IN LISTS FILES)
1809          if (NOT F MATCHES \"\\\\.buildinfo\")
1810            set (RC 1)
1811            if (NOT BUILDER MATCHES \"texinfo\")
1812              execute_process (
1813                COMMAND \"${CMAKE_COMMAND}\" -E compare_files
1814                    \"${SPHINX_OUTPUT_DIRECTORY}/\${SPHINX_BUILDER}/\${F}\"
1815                    \"\${CMAKE_INSTALL_PREFIX}/\${F}\"
1816                RESULT_VARIABLE RC
1817                OUTPUT_QUIET
1818                ERROR_QUIET
1819              )
1820            endif ()
1821            if (RC EQUAL 0)
1822              message (STATUS \"Up-to-date: \${CMAKE_INSTALL_PREFIX}/\${F}\")
1823            else ()
1824              message (STATUS \"Installing: \${CMAKE_INSTALL_PREFIX}/\${F}\")
1825              if (BUILDER MATCHES \"texinfo\")
1826                if (EXISTS \"\${CMAKE_INSTALL_PREFIX}/dir\")
1827                  execute_process (
1828                    COMMAND install-info
1829                        \"${SPHINX_OUTPUT_DIRECTORY}/\${SPHINX_BUILDER}/\${F}\"
1830                        \"\${CMAKE_INSTALL_PREFIX}/dir\"
1831                    RESULT_VARIABLE RC
1832                    OUTPUT_QUIET
1833                    ERROR_QUIET
1834                  )
1835                else ()
1836                  execute_process (
1837                    COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different
1838                        \"${SPHINX_OUTPUT_DIRECTORY}/\${SPHINX_BUILDER}/\${F}\"
1839                        \"\${CMAKE_INSTALL_PREFIX}/dir\"
1840                    RESULT_VARIABLE RC
1841                    OUTPUT_QUIET
1842                    ERROR_QUIET
1843                  )
1844                endif ()
1845              else ()
1846                execute_process (
1847                  COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different
1848                      \"${SPHINX_OUTPUT_DIRECTORY}/\${SPHINX_BUILDER}/\${F}\"
1849                      \"\${CMAKE_INSTALL_PREFIX}/\${F}\"
1850                  RESULT_VARIABLE RC
1851                  OUTPUT_QUIET
1852                  ERROR_QUIET
1853                )
1854              endif ()
1855              if (RC EQUAL 0)
1856                # also remember .info files for deinstallation via install-info --delete
1857                list (APPEND CMAKE_INSTALL_MANIFEST_FILES \"\${CMAKE_INSTALL_PREFIX}/\${F}\")
1858              else ()
1859                message (STATUS \"Failed to install \${CMAKE_INSTALL_PREFIX}/\${F}\")
1860              endif ()
1861            endif ()
1862          endif ()
1863        endforeach ()
1864      endfunction ()
1865
1866      set (BUILDERS \"${SPHINX_BUILDERS}\")
1867      set (HTML_INSTALLED FALSE)
1868      foreach (BUILDER IN LISTS BUILDERS)
1869        if ((BUILDER MATCHES \"html\" AND NOT HTML_INSTALLED) OR
1870              (BUILDER MATCHES \"texinfo|man\" AND UNIX) OR
1871              NOT BUILDER MATCHES \"html|texinfo|man|latex|linkcheck\")
1872          install_sphinx_doc (\${BUILDER})
1873          if (BUILDER MATCHES \"html\")
1874            set (HTML_INSTALLED TRUE)
1875          endif ()
1876        endif ()
1877      endforeach ()
1878      "
1879  )
1880  # done
1881  message (STATUS "Adding documentation ${TARGET_UID}... - done")
1882endfunction ()
1883
1884# ============================================================================
1885# change log
1886# ============================================================================
1887
1888# ----------------------------------------------------------------------------
1889## @brief Add target for generation of ChangeLog file.
1890#
1891# The ChangeLog is either generated from the Subversion or Git log depending
1892# on which revision control system is used by the project. Moreover, the
1893# project's source directory must be either a Subversion working copy or
1894# the root of a Git repository, respectively. In case of Subversion, if the
1895# command-line tool svn2cl(.sh) is installed, it is used to output a nicer
1896# formatted change log.
1897function (basis_add_changelog)
1898  _basis_make_target_uid (TARGET_UID changelog)
1899
1900  option (BUILD_CHANGELOG "Request build and/or installation of the ChangeLog." OFF)
1901  mark_as_advanced (BUILD_CHANGELOG)
1902  set (CHANGELOG_FILE "${PROJECT_BINARY_DIR}/ChangeLog")
1903
1904  message (STATUS "Adding ChangeLog...")
1905
1906  if (NOT PROJECT_IS_MODULE AND BUILD_CHANGELOG)
1907    set (_ALL "ALL")
1908  else ()
1909    set (_ALL)
1910  endif ()
1911
1912  set (DISABLE_BUILD_CHANGELOG FALSE)
1913
1914  # --------------------------------------------------------------------------
1915  # generate ChangeLog from Subversion history
1916  if (EXISTS "${PROJECT_SOURCE_DIR}/.svn")
1917    find_package (Subversion QUIET)
1918    if (Subversion_FOUND)
1919
1920      if (_ALL)
1921        message ("Generation of ChangeLog enabled as part of ALL."
1922                 " Be aware that the ChangeLog generation from the Subversion"
1923                 " commit history can take several minutes and may require the"
1924                 " input of your Subversion repository credentials during the"
1925                 " build. If you would like to build the ChangeLog separate"
1926                 " from the rest of the software package, disable the option"
1927                 " BUILD_CHANGELOG. You can then build the changelog target"
1928                 " separate from ALL.")
1929      endif ()
1930
1931      # using svn2cl command
1932      find_program (
1933        SVN2CL_EXECUTABLE
1934          NAMES svn2cl svn2cl.sh
1935          DOC   "The command line tool svn2cl."
1936      )
1937      mark_as_advanced (SVN2CL_EXECUTABLE)
1938      if (SVN2CL_EXECUTABLE)
1939        add_custom_target (
1940          ${TARGET_UID} ${_ALL}
1941          COMMAND "${SVN2CL_EXECUTABLE}"
1942              "--output=${CHANGELOG_FILE}"
1943              "--linelen=79"
1944              "--reparagraph"
1945              "--group-by-day"
1946              "--include-actions"
1947              "--separate-daylogs"
1948              "${PROJECT_SOURCE_DIR}"
1949          COMMAND "${CMAKE_COMMAND}"
1950              "-DCHANGELOG_FILE:FILE=${CHANGELOG_FILE}" -DINPUTFORMAT=SVN2CL
1951              -P "${BASIS_MODULE_PATH}/PostprocessChangeLog.cmake"
1952          WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
1953          COMMENT "Generating ChangeLog from Subversion log (using svn2cl)..."
1954        )
1955      # otherwise, use svn log output directly
1956      else ()
1957        add_custom_target (
1958          ${TARGET_UID} ${_ALL}
1959          COMMAND "${CMAKE_COMMAND}"
1960              "-DCOMMAND=${Subversion_SVN_EXECUTABLE};log"
1961              "-DWORKING_DIRECTORY=${PROJECT_SOURCE_DIR}"
1962              "-DOUTPUT_FILE=${CHANGELOG_FILE}"
1963              -P "${BASIS_SCRIPT_EXECUTE_PROCESS}"
1964          COMMAND "${CMAKE_COMMAND}"
1965              "-DCHANGELOG_FILE:FILE=${CHANGELOG_FILE}" -DINPUTFORMAT=SVN
1966              -P "${BASIS_MODULE_PATH}/PostprocessChangeLog.cmake"
1967          COMMENT "Generating ChangeLog from Subversion log..."
1968          VERBATIM
1969        )
1970      endif ()
1971
1972    else ()
1973      if (BASIS_VERBOSE)
1974        message (STATUS "Project is SVN working copy but Subversion executable was not found."
1975                        " Generation of ChangeLog disabled.")
1976      endif ()
1977      set (DISABLE_BUILD_CHANGELOG TRUE)
1978    endif ()
1979
1980  # --------------------------------------------------------------------------
1981  # generate ChangeLog from Git log
1982  elseif (EXISTS "${PROJECT_SOURCE_DIR}/.git")
1983    find_package (Git QUIET)
1984    if (GIT_FOUND)
1985
1986      add_custom_target (
1987        ${TARGET_UID} ${_ALL}
1988        COMMAND "${CMAKE_COMMAND}"
1989            "-DCOMMAND=${GIT_EXECUTABLE};log;--date-order;--date=short;--pretty=format:%ad\ \ %an%n%n%w(79,8,10)* %s%n%n%b%n"
1990            "-DWORKING_DIRECTORY=${PROJECT_SOURCE_DIR}"
1991            "-DOUTPUT_FILE=${CHANGELOG_FILE}"
1992            -P "${BASIS_SCRIPT_EXECUTE_PROCESS}"
1993        COMMAND "${CMAKE_COMMAND}"
1994            "-DCHANGELOG_FILE=${CHANGELOG_FILE}" -DINPUTFORMAT=GIT
1995            -P "${BASIS_MODULE_PATH}/PostprocessChangeLog.cmake"
1996        COMMENT "Generating ChangeLog from Git log..."
1997        VERBATIM
1998      )
1999
2000    else ()
2001      if (BASIS_VERBOSE)
2002        message (STATUS "Project is Git repository but Git executable was not found."
2003                        " Generation of ChangeLog disabled.")
2004      endif ()
2005      set (DISABLE_BUILD_CHANGELOG TRUE)
2006    endif ()
2007
2008  # --------------------------------------------------------------------------
2009  # neither SVN nor Git repository
2010  else ()
2011    if (BASIS_VERBOSE)
2012      message (STATUS "Project source directory ${PROJECT_SOURCE_DIR} is neither"
2013                      " SVN working copy nor Git repository. Generation of ChangeLog disabled.")
2014    endif ()
2015    set (DISABLE_BUILD_CHANGELOG TRUE)
2016  endif ()
2017
2018  # --------------------------------------------------------------------------
2019  # disable changelog target
2020  if (DISABLE_BUILD_CHANGELOG)
2021    set (BUILD_CHANGELOG OFF CACHE INTERNAL "" FORCE)
2022    message (STATUS "Adding ChangeLog... - skipped")
2023    return ()
2024  endif ()
2025
2026  # --------------------------------------------------------------------------
2027  # cleanup on "make clean"
2028  set_property (DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CHANGELOG_FILE}")
2029
2030  # --------------------------------------------------------------------------
2031  # install ChangeLog
2032  get_filename_component (CHANGELOG_NAME "${CHANGELOG_FILE}" NAME)
2033  if (PROJECT_IS_MODULE)
2034    set (CHANGELOG_NAME "${CHANGELOG_NAME}-${PROJECT_NAME}")
2035  endif ()
2036
2037  install (
2038    FILES       "${CHANGELOG_FILE}"
2039    DESTINATION "${TOPLEVEL_INSTALL_DOC_DIR}"
2040    COMPONENT   "${BASIS_RUNTIME_COMPONENT}"
2041    RENAME      "${CHANGELOG_NAME}"
2042    OPTIONAL
2043  )
2044
2045  message (STATUS "Adding ChangeLog... - done")
2046endfunction ()
2047