doxygen
doxygen copied to clipboard
Cmake: Documenting cmake script
I am sorry if this is already implemented, but I couldn't see it in the issues or the list of supported languages.
Description
Being able to generate xml and html documentation for cmake entities. These would be particularly useful with sphinx+breathe and moderncmakedomain
Details
A minimal documentation that would be helpful for the user documentation:
- [ ]
macro()andfunction(): positional arguments, named arguments (fromcmake_parsed_arguments), and expected variables (set from the parent when function/macro is called) - [ ]
option()andset(CACHE): type, default, helper string - [ ]
target: type, aliases, exported name, generated object (library and binary) if any - [ ]
install(COMPONENT): component, installed items - [ ]
install(FILES): files, destination - [ ]
export(TARGET)(part oftargetabove) - [ ]
find_package(COMPONENT): component, target file
Additionally I think these are helpful for the developer documentation:
- [ ]
add_custom_command(): output files/target, commands, comment - [ ]
add_custom_target(): target, commands, comment - [ ]
include(): location (e.g.my_helper.cmake)
When I understand it correctly you want to be able to document cmake files and present them in a way doxygen does.
There is no default support for these kind of files in doxygen, you probably can get some good results using a filter and translate the cmake files to something doxygen understands (like C++ code), with settings like INPUT_FILTER etc. this might be possible.
When I understand it correctly you want to be able to document cmake files and present them in a way doxygen does.
Indeed that's the case. We cannot map all of the cmake entities to C++ ones, but if we can define other custom entities in doxygen, maybe we can find some abstract way of building them. At least all of the definition syntax is statically defined, so it should be possible to connect the doxygen comments above a add_custom_command() for example.
Maybe you could give an example with the results you got so far and the problems you still encounter i.e.:
- attach a, small, self contained example (source+configuration file in a, compressed, tar or zip file) that allows us to see and reproduce the problem? Please don't add external links as they might not be persistent.
- specify the full doxygen version used (
doxygen -v).
Ok, I will make a small example project to mock-up these, but conceptually here is a short snippet of what I am referring to, just so we can confirm we are on the same page:
### This is a description of `my_function`
###
### @param _inp1 A positional argument
### @param VAR2 A named argument
function(my_function _inp1)
cmake_parse_arguments(ARGS "" "VAR2" "" ${ARGN})
...
endfunction()
(Just some nitpicking)
- The
:should not be present in the@paramstatement for the argument name VAR2is not an argument of the function
- VAR2 is not an argument of the function
Actually that's the thing about cmake. VAR2 is an argument of my_function because it is called via:
my_function(${positional_argument} VAR2 ${named_argument})
Initially I think it is ok if these are passed manually because these need to be parsed, e.g. finding the cmake_parse_arguments() expanding any definitions defined from set(), etc. It might be possible to use cmake-file-api to retrieve these, but I haven't researched this.
When I'm not mistaken the @param VAR2 will throw a warning as doxygen doesn't know about the argument VAR2 but it will show in the documentation.
Well, I've tried to to trick doxygen to parse it as a different language, and unsurprisingly it failed spectacularly:
Input file
Filename: test.cmake
### This is a description of `my_function`
###
### @param _inp1 A positional argument
### @param VAR2 A named argument
/// This is a description of `my_function`
///
/// @param _inp1 A positional argument
/// @param VAR2 A named argument
function(my_function _inp1)
cmake_parse_arguments(ARGS "" "VAR2" "" ${ARGN})
endfunction()
Doxyfile (Relevant parts)
GENERATE_XML = YES
INPUT = cmake/test.cmake
EXTENSION_MAPPING = cmake=c++
Generated xml
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.9.5" xml:lang="en-US">
<compounddef id="test_8cmake" kind="file" language="C++">
<compoundname>test.cmake</compoundname>
<sectiondef kind="func">
<memberdef kind="function" id="test_8cmake_1a44019289305eb00ccec292780743b509" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type></type>
<definition>function</definition>
<argsstring>(my_function _inp1) cmake_parse_arguments(ARGS "" "VAR2" "" $</argsstring>
<name>function</name>
<param>
<type>my_function</type>
<declname>_inp1</declname>
</param>
<briefdescription>
</briefdescription>
<detaileddescription>
<para>This is a description of <computeroutput>my_function</computeroutput></para>
<para><parameterlist kind="param"><parameteritem>
<parameternamelist>
<parametername>_inp1</parametername>
</parameternamelist>
<parameterdescription>
<para>A positional argument </para>
</parameterdescription>
</parameteritem>
<parameteritem>
<parameternamelist>
<parametername>VAR2</parametername>
</parameternamelist>
<parameterdescription>
<para>A named argument </para>
</parameterdescription>
</parameteritem>
</parameterlist>
</para>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="cmake/test.cmake" line="9" column="1" bodyfile="cmake/test.cmake" bodystart="9" bodyend="10"/>
</memberdef>
</sectiondef>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<programlisting>
<codeline lineno="1"><highlight class="preprocessor">###<sp/>This<sp/>is<sp/>a<sp/>description<sp/>of<sp/>`my_function`</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="2"><highlight class="normal"></highlight><highlight class="preprocessor">###</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="3"><highlight class="normal"></highlight><highlight class="preprocessor">###<sp/>@param<sp/>_inp1<sp/>A<sp/>positional<sp/>argument</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="4"><highlight class="normal"></highlight><highlight class="preprocessor">###<sp/>@param<sp/>VAR2<sp/>A<sp/>named<sp/>argument</highlight></codeline>
<codeline lineno="9"><highlight class="normal">function(my_function<sp/>_inp1)</highlight></codeline>
<codeline lineno="10"><highlight class="normal"><sp/><sp/><sp/><sp/>cmake_parse_arguments(ARGS<sp/></highlight><highlight class="stringliteral">""</highlight><highlight class="normal"><sp/></highlight><highlight class="stringliteral">"VAR2"</highlight><highlight class="normal"><sp/></highlight><highlight class="stringliteral">""</highlight><highlight class="normal"><sp/>${ARGN})</highlight></codeline>
<codeline lineno="11"><highlight class="normal">endfunction()</highlight></codeline>
</programlisting>
<location file="cmake/test.cmake"/>
</compounddef>
</doxygen>
So there are a few things to fix:
- [ ] Use the cmake comment token:
# - [ ] Change the parsing to extract the type/definition from the function caller, i.e.
add_custom_target()->target. There is no type prefix likedef my_function:in python - [ ] Parse
bodystart,bodyendaccordingly (look forendfunction(),endmacro()or closing parenthesis)) - [ ] Change
languageincompounddeftocmake - [ ] Disable
argstringor compute it includingcmake_parse_argumentorcmake-file-api(currently not possible there either) - [ ] Disable or fix the code highlighter
In the https://github.com/doxygen/doxygen/issues/9906#issuecomment-1464114535I mentioned:
There is no default support for these kind of files in doxygen, you probably can get some good results using a filter and translate the cmake files to something doxygen understands (like C++ code), with settings like INPUT_FILTER etc. this might be possible.
but in the presented "Doxyfile (Relevant parts)" (better to use the outcome of doxygen -x Doxyfile as this shows the differences with the default settings), there is nothing about a filter so doxygen sees the cmake file as C++ code and the cmake file is no C++ code. You will probably have had some warnings as well.
There is EXTENSION_MAPPING to map it to c++ code, and that is why it picks up at /// "comments". But C++ parser is ill-suited for building on top of this because:
- It has different comment tokens
- It requires a type prefix: i.e.
voidinvoid my_function(),classinclass my_class, etc.
You will probably have had some warnings as well.
Of course there were warnings like that, and surprisingly only for VAR2, even though _inp1 should also be ill defined there.
When having cmake code like:
### This is a description of `my_function`
###
### @param _inp1 A positional argument
### @param VAR2 A named argument
function(my_function _inp1)
cmake_parse_arguments(ARGS "" "VAR2" "" ${ARGN})
...
endfunction()
this would be in C++ terms something like:
/// This is a description of `my_function`
///
/// @param _inp1 A positional argument
/// @param VAR2 A named argument
void my_function(char* _inp1)
cmake_parse_arguments(ARGS "" "VAR2" "" ${ARGN});
...
}
and this should be done by the filter (probably the filter should even do more).
Doxygen does not have any parser for cmake, so it does not know how to handle any of this. If I use the first version, no xml output is produced at all. There is no cmake related filter:
- Not in
FILE_PATTERNSbecause it is not built-in INPUT_FILTERrequires a custom parser
But even if we change it to be c++ like function, that doesn't solve the main issues that a new language format needs to be defined in order to address all of the other checkboxed issues. For example, how do we convert option() or set(CACHE). How about add_library() and find_package()?
You have to write the filter yourself, doxygen doesn't provide the filter just the possibility to use it.
Again, INPUT_FILTER is not appropriate due to how different these are. The xml output format is fine and useful to output to breathe to generate documentations, but the parser itself needs to be reformulated. There are no return type, you should distinguish between option(), set(CACHE), set(PARENT_SCOPE), etc.
Did you try a filter? If so please show the filter, the used input code and the used settings (i.e. the result of doxygen -x Doxyfile) as well as the used doxygen version (doxygen -v) all in a self contained example (source+configuration file in a, compressed, tar or zip file) that allows us to reproduce the problem? Please don't add external links as they might not be persistent.
No, I did not write a filter because: how do we translate a option() to c++ like entity, how do we add additional important information to be exported, e.g. CACHE or PARENT_SCOPE export type. The design needs to be discussed first. And I am suggesting that a new language needs to be defined instead of writing a filter.
I would welcome such a feature. As a workaround, I always use now this cmake module: https://github.com/ferdymercury/cmake-modules/blob/main/make_documentation.cmakewhich parses the file and creates a dox. I use it to document all cmake options. I create the table by hand in the file header, as a comment, normally duplicating the info already written in the help tag of cmake-option command. Such documentation is essential for developers to know what flags they can configure while building.
But a builtin solution would be cleaner, so that it would directly parse the options instead of having to copy paste so much.
Below one example of how i do it:
## CMAKE_DOCUMENTATION_START CMakeLists.txt
##
## Main CMakeFile for compiling MyProjectName.
## Following variables can be configured when running ccmake:
## <table>
## <caption id="config-cmake">Table of configurable CMake parameters</caption>
## <tr><th>Variable <th>Values <th>Description
## <tr><td>BUILD_DOCUMENTATION <td>ON (OFF) <td>Build Doxygen HTML documentation
## <tr><td>CLI11_DIR <td>/opt/CLI11 <td>CLI11 git repository
## <tr><td>CMAKE_BUILD_TYPE <td>Release (Debug) <td>Choose the type of build
## <tr><td>ENABLE_TESTING <td>ON (OFF) <td>Build CTests
## <tr><td>QHG_LOCATION <td>qhelpgenerator <td>Path to qhelpgenerator
## <tr><td>ROOT_DIR <td>$ROOTSYS (/opt/root) <td>ROOT build directory
## </table>
##
## CMAKE_DOCUMENTATION_END
option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ON)
#and all the other options here duplicated, etc, other stuff...
add_custom_target(dox
#DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/doxygen.stamp
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
)
include(make_documentation)
PARSE_CMAKE_DOCUMENTATION(INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt" EXCLUDES "${CMAKE_CURRENT_BINARY_DIR}/*")
WRITE_CMAKE_DOCUMENTATION( "${CMAKE_CURRENT_SOURCE_DIR}/cmake.dox" SORTED )
I know nothing about doxygen, and that will show in my answer here, but if cmake had block comments, couldn't we just mark the beginning and of a bl;ock and embed doxygen comments inside? Something like this
Be cheeky, just use /** to introduce a block comment
Use **/ to terminate a block comment
/**
* @brief a function to create a depencancy and add an include directory to it
* @param foo the target to work on
* @param bar the dependancy
* @param baz the include folder to add to @param(foo)
**/
function(myFunc foo bar baz)
add_depencies(${foo} ${bar})
target_include_dir(${foo} ${baz})
endfunction()
It might be possible when you also use a filter as also indicated in the comment https://github.com/doxygen/doxygen/issues/9906#issuecomment-1466183889