# Define add_sphinx_document() and helpers. # Is a helper macro for add_sphinx_document() to add the arguments passed to # the macro to all the commands that will be passed to add_custom_target(). macro(sphinx_helper_add_to_commands) if(Sphinx_FOUND) list(APPEND _COMMAND ${ARGV}) foreach(_X ${_EXTRA_COMMANDS}) list(APPEND ${_X} ${ARGV}) endforeach() endif() endmacro() # Is a helper macro for add_sphinx_document() to set the first argument to # the path in the second argument with the third argument used as the base # for relative paths. If the fourth argument evaluates to be true, the # path will be converted to the native format. macro(sphinx_helper_convert_path _OUT _IN _BASE _NATIVE) if("${_IN}" MATCHES "^[/~]") if(${_NATIVE}) file(TO_NATIVE_PATH "${_IN}" ${_OUT}) else() set(${_OUT} "${_IN}") endif() elseif("${_BASE}" MATCHES "/$") if(${_NATIVE}) file(TO_NATIVE_PATH "${_BASE}${_IN}" ${_OUT}) else() set(${_OUT} "${_BASE}${_IN}") endif() else() if(${_NATIVE}) file(TO_NATIVE_PATH "${_BASE}/${_IN}" ${_OUT}) else() set(${_OUT} "${_BASE}/${_IN}") endif() endif() endmacro() # add_sphinx_document( # # SOURCE_DIR | # SOURCE_TARGET [SOURCE_TARGET_DIR ] # [[NOCONF] [[CONF_DIR [CONF_TARGET_DIR ]]]] # [OUTPUT_FORMATS ...] # [OUTPUT_DIR ] # [CONF_OPTIONS ...] # [ALL] # [BUILD_FAIL_NO_SPHINX] [BUILD_MESSAGE_NO_SPHINX message_part ...] # ) # is the name of the target that will build a set of documention # with sphinx-build. The target will be created with add_custom_target(). # If sphinx-build was not found with find_package(Sphinx), the target will # generate a message, customizable with BUILD_MESSAGE_NO_SPHINX, and, if # configured with BUILD_FAIL_NO_SPHINX, cause that build step to fail. # SOURCE_DIR specifies that is the source directory to be used # with sphinx-build for the target. If is a relative path, # it is evaluated with respect to CMAKE_CURRENT_SOURCE_DIR. # SOURCE_TARGET specifies that the source directory for the target will be # generated by another target, , created by # add_custom_target(). SOURCE_TARGET_DIR specifies the directory, # , that is created by and will be passed # to sphinx-build as the source directory for the documentation. If # is a relative path, it is interpreted relative to the # value of the RUNTIME_OUTPUT_DIRECTORY property of . # If SOURCE_TARGET_DIR is not present, the source directory will be the # value of the RUNTIME_OUTPUT_DIRECTORY property of . # will be added as a prerequisite for with # add_dependencies(). # NOCONF specifies that the target will not have a configuration file and # all configuration will have to be done via the options set with # CONF_OPTIONS. # CONF_DIR specifies that is the directory with the conf.py file # that sphinx-build should use for the target. If is a # relative path, it is evaluated with respect to CMAKE_CURRENT_SOURCE_DIR. # If NOCONF is not set and neither CONF_DIR nor CONF_TARGET are used, the # path to conf.py in the directory set by SOURCE_DIR or SOURCE_TARGET. # CONF_TARGET specifies that the configuration file for the target will be # generated by another target, , created by # add_custom_target(). CONF_TARGET_DIR specifies the directory, # , that is created by and will be passed as the # location of the conf.py file to sphinx-build. If is a # relative path, it will be interpreted relative to the value of the # RUNTIME_OUTPUT_DIRECTORY property of . If CONF_TARGET_DIR # is not present, the path to conf.py will be the value of the # RUNTIME_OUTPUT_DIRECTORY property of . # will be added as a prerequisite for with # add_dependencies(). If NOCONF is not set and neither CONF_DIR nor # CONF_TARGET are used, the path to conf.py will be the directory set by # SOURCE_DIR or SOURCE_TARGET. # OUTPUT_FORMATS specifies the output formats that the target will generate. # Each output format must be an allowed value for the "-b" option to # sphinx-build. One a target is created, use # sphinx_document_get_output_formats() on it to get the formats it will # generate. # OUTPUT_DIR specifies that is the base directory for where # results and intermediates will be written. If is a relative # path, it will be interpreted relative to CMAKE_CURRENT_BINARY_DIR. # If not set, the base output directory will be # CMAKE_RUNTIME_OUTPUT_DIRECTORY. Once a target is created, use # sphinx_document_get_output_directory() on it to get the subdirectory of # the base directory where the generated documentation will be written. # Use sphinx_document_get_output_formats() to get the list of the names # of the output formats generated. The result for each format will be # stored in a subdirectory, with the same name as the name of the format, # of the directory returned by sphinx_document_get_output_directory(). # CONF_OPTIONS specifies one or more options to override what is in the # configuration file: each option will be passed to sphinx-build with # a "-D" option. # ALL causes the generated target to be added to the default build target. # BUILD_FAIL_NO_SPHINX causes the target to fail if triggered during a build # and sphinx-build was not available. # BUILD_MESSAGE_NO_SPHINX customizes the message shown when the target is # triggered during a build and sphinx-build was not available. Set the # message to an empty string to not have any message printed. # # Usage notes: # If you are using CONF_TARGET to pick up a conf.py generated in the build # directories, the things that conf.py pulls in, like the _static or # _templates directories, will have to be copied to the build directory # (or generated some other way there) so they can be found by sphinx-build # at the same locations relative to conf.py. # # Limitations: # Currently provides no way to allow the caller to pass "-a", "-E", "-t", # "-j", "-A", "-n", "-N", "-v", "-q", "-Q", "-w", "-W", "--keep-going", # "-T", or "-P" options to sphinx-build. # function(add_sphinx_document _TARGET_NAME) find_package(Sphinx) # Parse the arguments. set( _OPTIONS NOCONF ALL BUILD_FAIL_NO_SPHINX ) set( _ONE_VALUE_ARGS SOURCE_DIR SOURCE_TARGET SOURCE_TARGET_DIR CONF_DIR CONF_TARGET CONF_TARGET_DIR OUTPUT_DIR ) set( _MULT_VALUE_ARGS OUTPUT_FORMATS CONF_OPTIONS BUILD_MESSAGE_NO_SPHINX ) cmake_parse_arguments(_IN "${_OPTIONS}" "${_ONE_VALUE_ARGS}" "${_MULT_VALUE_ARGS}" ${ARGN}) if(DEFINED _IN_UNPARSED_ARGUMENTS) message(FATAL_ERROR "add_sphinx_document() was called with one or more unrecognized arguments: ${_IN_UNPARSED_ARGUMENTS}") endif() # Initialize a list of the target dependencies. unset(_TARGET_DEPENDS) # Initialize the list, _COMMAND, that will be the first command passed to # add_custom_target(). Also initialize the list of the other commands that # will be passed. unset(_EXTRA_COMMANDS) set(_EXTRA_INDEX -1) if(Sphinx_FOUND) set(_COMMAND "${SPHINX_EXECUTABLE}") else() if(DEFINED _IN_BUILD_MESSAGE_NO_SPHINX) if(NOT _IN_BUILD_MESSAGE_NO_SPHINX STREQUAL "") set( _COMMAND "${CMAKE_COMMAND}" "-E" "echo" ${_IN_BUILD_MESSAGE_NO_SPHINX} ) else() unset(_COMMAND) endif() else() set( _COMMAND "${CMAKE_COMMAND}" "-E" "echo" "sphinx-build not found; cannot build ${_TARGET_NAME}" ) endif() if(_IN_BUILD_FAIL_NO_SPHINX) if(DEFINED _COMMAND) math(EXPR _EXTRA_INDEX "${_EXTRA_INDEX} + 1") set(_Y "_EXTRA_COMMAND_${_EXTRA_INDEX}") list(APPEND _EXTRA_COMMANDS ${_Y}) set(${_Y} "${CMAKE_COMMAND}" "-E" "false") else() set(_COMMAND "${CMAKE_COMMAND}" "-E" "false") endif() endif() endif() if((DEFINED _IN_OUTPUT_FORMATS) AND Sphinx_FOUND) foreach(_X ${_IN_OUTPUT_FORMATS}) if(_EXTRA_INDEX EQUAL -1) list(APPEND _COMMAND "-b" "${_X}") else() set(_Y "_EXTRA_COMMAND_${_EXTRA_INDEX}") list(APPEND _EXTRA_COMMANDS "${_Y}") set(${_Y} "${SPHINX_EXECUTABLE}" "-b" "${_X}") endif() math(EXPR _EXTRA_INDEX "${_EXTRA_INDEX} + 1") endforeach() endif() # Handle the source directory. if(DEFINED _IN_SOURCE_DIR) if((DEFINED _IN_SOURCE_TARGET) OR (DEFINED _IN_SOURCE_TARGET_DIR)) message(FATAL_ERROR "add_sphinx_document() was called with SOURCE_DIR and one or more of SOURCE_TARGET and SOURCE_TARGET_DIR") endif() sphinx_helper_convert_path( _NATIVE_SOURCE_DIR "${_IN_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" YES ) elseif(DEFINED _IN_SOURCE_TARGET) if(NOT DEFINED _IN_SOURCE_TARGET_DIR) set(_IN_SOURCE_TARGET_DIR "") endif() get_property( _X TARGET ${_IN_SOURCE_TARGET} PROPERTY "RUNTIME_OUTPUT_DIRECTORY" ) sphinx_helper_convert_path( _NATIVE_SOURCE_TARGET_DIR "${_IN_SOURCE_TARGET_DIR}" "${_X}" YES ) list(APPEND _TARGET_DEPENDS "${_IN_SOURCE_TARGET}") else() message(FATAL_ERROR "add_sphinx_document() was called without either SOURCE_DIR or SOURCE_TARGET") endif() # Handle the configuration file. if(_IN_NOCONF) if((DEFINED _IN_CONF_DIR) OR (DEFINED _IN_CONF_TARGET) OR (DEFINED _IN_CONF_TARGET_DIR)) message(FATAL_ERROR "add_sphinx_document() was called with NOCONF and one or more of CONF_DIR, CONF_TARGET, or CONF_TARGET_DIR") endif() sphinx_helper_add_to_commands("-C") elseif(DEFINED _IN_CONF_DIR) if(DEFINED _IN_CONF_TARGET) message(FATAL_ERROR "add_sphinx_document() was called with both CONF_DIR and CONF_TARGET") endif() sphinx_helper_convert_path( _NATIVE_CONF_DIR "${_IN_CONF_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" YES ) elseif(DEFINED _IN_CONF_TARGET) if(NOT DEFINED _IN_CONF_TARGET_DIR) set(_IN_CONF_TARGET_DIR "") endif() get_property( _X TARGET ${_IN_CONF_TARGET} PROPERTY "RUNTIME_OUTPUT_DIRECTORY" ) sphinx_helper_convert_path( _NATIVE_CONF_TARGET_DIR "${_IN_CONF_TARGET_DIR}" "${_X}" YES ) list(APPEND _TARGET_DEPENDS "${_IN_CONF_TARGET}") sphinx_helper_add_to_commands("-c" "${_NATIVE_CONF_TARGET_DIR}") else() if(DEFINED _IN_SOURCE_DIR) set(_IN_CONF_DIR "${_IN_SOURCE_DIR}") set(_X "${CMAKE_CURRENT_SOURCE_DIR}") else() set(_IN_CONF_DIR "${_IN_SOURCE_TARGET_DIR}") get_property( _X TARGET ${_IN_CONF_TARGET} PROPERTY "RUNTIME_OUTPUT_DIRECTORY" ) endif() sphinx_helper_convert_path( _NATIVE_CONF_DIR "${_IN_CONF_DIR}" "${_X}" YES ) endif() # Handle the output directory. if(NOT (DEFINED _IN_OUTPUT_DIR)) set(_IN_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") endif() sphinx_helper_convert_path( _OUT_OUTPUT_BASE_DIR "${_IN_OUTPUT_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" NO ) sphinx_helper_convert_path( _NATIVE_OUTPUT_CACHE_DIR "_doctree" "${_OUT_OUTPUT_BASE_DIR}" YES ) sphinx_helper_add_to_commands("-d" "${_NATIVE_OUTPUT_CACHE_DIR}") # Handle the overrides for values set in the configuration file. if(DEFINED _IN_CONF_OPTIONS) foreach(_X ${_IN_CONF_OPTIONS}) sphinx_helper_add_to_commands("-D" "${_X}") endforeach() endif() # Add the source directory as the first non-option to the command. if(DEFINED _IN_SOURCE_DIR) sphinx_helper_add_to_commands("${_NATIVE_SOURCE_DIR}") else() sphinx_helper_add_to_commands("${_NATIVE_SOURCE_TARGET_DIR}") endif() # Add the output directory for documents as the second non-option to the # the command. Put each format in a separate directory, like the make # mode for sphinx-build does. if((DEFINED _IN_OUTPUT_FORMATS) AND Sphinx_FOUND) set(_EXTRA_INDEX -1) foreach(_X ${_IN_OUTPUT_FORMATS}) sphinx_helper_convert_path( _NATIVE_OUTPUT_DOC_DIR "${_X}" "${_OUT_OUTPUT_BASE_DIR}" YES ) if(_EXTRA_INDEX EQUAL -1) list(APPEND _COMMAND "${_NATIVE_OUTPUT_DOC_DIR}") else() set(_Y "_EXTRA_COMMAND_${_EXTRA_INDEX}") list(APPEND ${_Y} "${_NATIVE_OUTPUT_DOC_DIR}") endif() math(EXPR _EXTRA_INDEX "${_EXTRA_INDEX} + 1") endforeach() elseif(Sphinx_FOUND) # There were no formats specified so use the bare output directory. sphinx_helper_convert_path( _NATIVE_OUTPUT_DOC_DIR "${_OUT_OUTPUT_BASE_DIR}" "" YES ) sphinx_helper_add_to_commands("${_NATIVE_OUTPUT_DOC_DIR}") endif() # Convert some variables for convenient expansion in create_custom_target(). if(_IN_ALL) set(_IN_ALL "ALL") else() set(_IN_ALL "") endif() set(_EXTRA "") if(NOT (_EXTRA_COMMANDS STREQUAL "")) foreach(_X ${_EXTRA_COMMANDS}) set(_EXTRA ${_EXTRA} "COMMAND" ${${_X}}) endforeach() endif() # Create the target. Modify and restore CMAKE_RUNTIME_OUTPUT_DIRECTORY # so that the target's RUNTIME_OUTPUT_DIRECTORY property reflects what # was set by the OUTPUT_DIR argument. if(DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(_OLD "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") else() unset(_OLD) endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${_OUT_OUTPUT_BASE_DIR}") add_custom_target(${_TARGET_NAME} ${_IN_ALL} ${_COMMAND} ${_EXTRA} VERBATIM) if(DEFINED _OLD) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${_OLD}") else() unset(CMAKE_RUNTIME_OUTPUT_DIRECTORY) endif() # Add the target dependencies. if(NOT ("${_TARGET_DEPENDS}" STREQUAL "")) ADD_DEPENDENCIES(${_TARGET_NAME} ${_TARGET_DEPENDS}) endif() # Define properties on the target to describe the placement of the output. # That's to allow downstream targets (installation or packaging, for # instance) to query it with sphinx_document_get_output_directory() and # sphinx_document_get_output_formats(). get_property( _PROP_DEFINED TARGET ${_TARGET_NAME} PROPERTY "SPHINX_OUTPUT_DIRECTORY" DEFINED ) if(NOT _PROP_DEFINED) define_property( TARGET PROPERTY "SPHINX_OUTPUT_DIRECTORY" BRIEF_DOCS "output directory for documention generated by sphinx-build" FULL_DOCS "Is the output directory associated with a target for documentation generated by sphinx-build. The path is a full one and is in CMake's format. The property reflects the directory configure into the target; changing the property won't affect the behavior of the target." ) endif() set_property( TARGET ${_TARGET_NAME} PROPERTY "SPHINX_OUTPUT_DIRECTORY" "${_OUT_OUTPUT_BASE_DIR}" ) get_property( _PROP_DEFINED TARGET ${_TARGET_NAME} PROPERTY "SPHINX_OUTPUT_FORMATS" DEFINED ) if(NOT _PROP_DEFINED) define_property( TARGET PROPERTY "SPHINX_OUTPUT_FORMATS" BRIEF_DOCS "list of output formats generated by sphinx-build" FULL_DOCS "Is the list of output formats generate by sphinx-build for a target. The output for each format will be in its own subdirectory of the directory named by the target's SPHINX_OUTPUT_DIRECTORY property. The name of the subdirectory will be the same as the name of the format. The property reflects which formats were configured into the target; changing the property won't affect the behavior of the target." ) endif() if(DEFINED _IN_OUTPUT_FORMATS) set_property( TARGET ${_TARGET_NAME} PROPERTY "SPHINX_OUTPUT_FORMATS" ${_IN_OUTPUT_FORMATS} ) endif() endfunction() # sphinx_document_get_output_directory( target) # Queries the given target for its output directory with the documentation # generated by sphinx-build. If that has not been set, it will set # to -NOTFOUND. Otherwise it will set to the path to the # directory. function(sphinx_document_get_output_directory _OUT _TARGET) get_target_property(_RESULT ${_TARGET} "SPHINX_OUTPUT_DIRECTORY") set(${_OUT} "${_RESULT}" PARENT_SCOPE) endfunction() # sphinx_document_get_output_formats( target) # Queries the given target for its list of output formats generated by # sphinx-build. If that has not been set, it will set to # -NOTFOUND. Otherwise it will set to the list of the formats. # The location of the documentation generated for a particular format will # be a subdirectory, with the same name as the name of the format, of the # directory returned by sphinx_document_get_output_directory(). function(sphinx_document_get_output_formats _OUT _TARGET) get_target_property(_RESULT ${_TARGET} "SPHINX_OUTPUT_FORMATS") set(${_OUT} "${_RESULT}" PARENT_SCOPE) endfunction()