CMakePP / CMinx

Generates API documentation for CMake functions and macros
https://cmakepp.github.io/CMinx/
Apache License 2.0
14 stars 5 forks source link

Is it possible to generate Documentation for `.cmake` file in general ? #133

Closed dschiller closed 1 year ago

dschiller commented 1 year ago

An example:

#[[[ ??? Somehow mark this block to be used for the .cmake file instead for `funcA` ???
# This is the cmake documentation.
#]]

function(funcA paramA)
  message(I am a function)
endfunction()

#[[[
# This function has a documentation.
# :param paramA: Help for `paramA`
# :param paramB: This is **paramB**
#]]
function(funcB paramA paramB)
  message(Anything)
endfunction()
ryanmrichard commented 1 year ago

I think this is a good suggestion and I feel like it'd be relatively easy to implement this.

Based a quick search to see what Sphinx-flavored reST does for this scenario (if anything) I learned:

Since .cmake files define CMake modules, I guess my suggestion is:

#[[[ .. cminx:module:: <module_name> 
#
#       Somehow mark this block to be used for the .cmake file instead for `funcA` ???
#       This is the cmake documentation.
#]]

I considered .. cmake:module::, but there are Sphinx domains for CMake out there (for example). Admittedly, most of the CMake domains I found seem to be abandon projects, but nonetheless I don't want to run the risk of conflicting with them in case someone still uses them.

Ultimately we could make the directive a configuration option that defaults to cminx:module::.

Thoughts?

AutonomicPerfectionist commented 1 year ago

I think this is a good suggestion and I feel like it'd be relatively easy to implement this.

Based a quick search to see what Sphinx-flavored reST does for this scenario (if anything) I learned:

  • :file: is already a recognized role, so we can't use that to denote it.
  • In the Python domain there is the .. py:module:: name directive (see here)

Since .cmake files define CMake modules, I guess my suggestion is:

#[[[ .. cminx:module:: <module_name> 
#
#       Somehow mark this block to be used for the .cmake file instead for `funcA` ???
#       This is the cmake documentation.
#]]

I considered .. cmake:module::, but there are Sphinx domains for CMake out there (for example). Admittedly, most of the CMake domains I found seem to be abandon projects, but nonetheless I don't want to run the risk of conflicting with them in case someone still uses them.

Ultimately we could make the directive a configuration option that defaults to cminx:module::.

Thoughts?

So to support a module-wide docstring we'll have to either require the docstring to be on like include_guard() calls or modify the aggregator to scan for doccomments not attached to commands. The docstring will have to include some type of marker to differentiate it from a docstring on a command. The marker could certainly be .. cminx:module:: <module_name> however that does carry the risk of implying a cminx domain to the user. We could also make the default marker something entirely different from RST to signal to the user that its a cminx-specific capability and won't be present in the generated RST, something like @cminx:module_doc

ryanmrichard commented 1 year ago

I guess I hadn't thought about it before, but how hard would it be to actually add a CMinx domain?

AutonomicPerfectionist commented 1 year ago

I will look into it and get back to you on that

AutonomicPerfectionist commented 1 year ago

@ryanmrichard I've looked through the Sphinx documentation and have a general idea of how to make a domain, unfortunately the documentation is fairly sparse but there are quite a few examples in sphinx-contrib. The main issue I see is that adding and relying on a custom domain means the generated RST won't be usable without the domain installed, so the standalone version of cminx will still require installing a dependency anyway.

ryanmrichard commented 1 year ago

@AutonomicPerfectionist Thanks for looking into that.

Admittedly, in my original comment I was only focused on what the doccomment would have to look like. I totally spaced on the fact that the RST resulting from the doccomment needs to be understandable by Sphinx.

I just took a look at the examples in this repo. Consider the file test_samples/quickstart.cmake:

#[[[
# A brief description.
#
# A more detailed description, must be separated from the brief by at least
# one blank line.
#
# :param param0: The 0-th parameter passed to the function
#]]
function(quickstart param0)
    message("This function says: ${param0}")
endfunction()

This already generates an RST which starts with something like:

#######################
test_samples.quickstart
#######################

.. module:: test_samples.quickstart

I'm wondering if we can just piggy back off of the ..module:: directive like:

#[[[ .. module:: test_samples.quickstart
#        
#       Documentation for the file goes here.  
#]]       

#[[[
# A brief description.
#
# A more detailed description, must be separated from the brief by at least
# one blank line.
#
# :param param0: The 0-th parameter passed to the function
#]]
function(quickstart param0)
    message("This function says: ${param0}")
endfunction()

and have that generate:


#######################
test_samples.quickstart
#######################

.. module:: test_samples.quickstart

   Documentation for the file goes here.

.. function:: quickstart(param0)

   A brief description.

   A more detailed description, must be separated from the brief by at least
   one blank line.

   :param param0: The 0-th parameter passed to the function

Admittedly, off the top of my head I'm not sure if that's legal RST, and if it is, if that accomplishes what we want.

AutonomicPerfectionist commented 1 year ago

It appears that having body content in the .. module:: directive is supported, but only for Sphinx versions of 5.2 or higher, released September 24th of this year. If Sphinx is installed via pip it should pull in 5.3, but if using the distro package manager it'll probably pull in a much older version. On older versions the RST still compiles, the content under the module directive just doesn't show up

AutonomicPerfectionist commented 1 year ago

I think putting the module documentation in the body of .. module:: directives is the most elegant solution, and we can put in our documentation the requirement of the more up-to-date sphinx version for that feature

ryanmrichard commented 1 year ago

@AutonomicPerfectionist that all sounds good to me.

AutonomicPerfectionist commented 1 year ago

I've figured out a few different implementations that will work. The first adds a new lexer token for the module doccomment; this is the easiest to implement and doesn't introduce any ambiguity to the grammar. It has limitations in how it's used in the CMake file, though. It must be the first element of the file and the .. module:: line must appear on the first line of the comment and must be formatted exactly, like this:

#[[[ .. module:: test_module
# This is the body of the module documentation.
#]]

The second is a variation of the first that allows the .. module:: line to be placed in the first two lines, but this introduces a bit of performance loss in the general case as doccomments must always have several additional characters looked ahead to determine whether it's a module doc or not. This is because Antlr4 does not allow a token to match on the beginning of input by design, so even if we want to say "only doccomments at the beginning of the file can be module doccomments," they will still all be checked regardless.

The third eschews a specialized Module_doc token entirely and uses the regular Docstring token. However, doing so introduces ambiguity into the grammar as this:

#[[[ .. module:: test_module
# Module doc
#]]
include_guard()

Is ambiguous about whether the doccomment is attached to the command or not. The solution is to ignore the ambiguity and allow the parser to attach it to the command and then check for the .. module:: line later inside the visitor code. This introduces a bit more complexity in the implementation, as we must check each Documented_command token we visit. The advantage of this design though is the string used to determine whether it's a module doc can be configured at runtime, i.e. through config files, whereas the lexer implementations are set in stone at lexer generation.

Additionally, for all 3 implementations, the .. module:: line is not inserted verbatim but is rather parsed, the module name extracted, and passed to an RSTWriter Directive object that handles the body text indentation, so indentation in the source doccomment is not required. Since it is not required, it might be advisable to use a special syntax to mark the module doccomment separate from RST so users aren't confused by the more lax indentation that is bound to occur in the wild. Doing so would also allow CMinx to continue autogenerating the module name so it stays in sync. A simple syntax could be the following:

#[[[ @module
# Module documentation
#]]

Another could be:


#[[[module]
# Body
#]]
ryanmrichard commented 1 year ago

@AutonomicPerfectionist thanks for looking into this. If the .. module:: line is not going to be inserted verbatim I can definitely see how problems could potentially arise down the road if a user adds does something fancy. So I guess I'm leaning towards the @module suggestion for all the reasons you suggested and because @ syntax is fairly common in other documentation markup languages.

dschiller commented 1 year ago

Thank you Ryan and Branden a lot for your work!

dschiller commented 1 year ago

The new documentation ( docs/source/documenting/module.rst ) is not in the actual documentation at https://cmakepp.github.io/CMinx/documenting/module.html - that will happen in a later stage ?

AutonomicPerfectionist commented 1 year ago

Yeah, the workflow that is supposed to automatically generate and deploy the documentation didn't fire along with the tag and release workflow, don't really have any leads on why that happened yet but tomorrow we can manually trigger those

dschiller commented 1 year ago

Is this already implemented in cminx 1.1.0 ?

test.cmake

#[[[ @module
# Module documentation
#]]

include(anotherfile) # for EXTERNAL_ROOT

test.rst


#############################################################################
/Volumes/K2TB/Work/Coding/tools/develop/test
#############################################################################

.. module:: /Volumes/K2TB/Work/Coding/tools/develop/test

.. function:: include(anotherfile)

   .. warning:: This is a generic command invocation. It is not a function or macro definition.

   @module
   Module documentation

How can I find out which is the latest version of cminx ? When I install it with pip I see 1.1.0:

pip install -U cminx
Requirement already satisfied: cminx in ./.venv/lib/python3.8/site-packages (1.1.0)

But the page for the documentation at https://cmakepp.github.io/CMinx uses a version 1.1.3. Is 1.1.3 the version of cminx or the documentation ?

AutonomicPerfectionist commented 1 year ago

There's a problem with the pip release action that I've been trying to figure out, if you need that functionality right now I can ask Ryan to push a release manually since I don't think I have the key for it

dschiller commented 1 year ago

Thanks Branden, I can wait. No need to push the release manually.