-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
`sphinx-c-apidoc` will generate C documentation files for a provided directory. closes #5
- Loading branch information
1 parent
31a2c2f
commit 4211a01
Showing
11 changed files
with
713 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
apidoc | ||
====== | ||
|
||
The `sphinx-c-apidoc` provides a way to generate documentation files for a C directory. | ||
It is meant to fulfill a similar role to the `sphinx-apidoc`_ command for python | ||
packages. | ||
|
||
.. autoprogram:: sphinx_c_autodoc.apidoc:get_parser() | ||
:prog: sphinx-c-apidoc | ||
|
||
Generated Documentation Files | ||
----------------------------- | ||
|
||
The generated documentation files will follow the same directory structure of the | ||
provide C source directory. | ||
|
||
For example: | ||
|
||
.. code-block:: text | ||
a_project | ||
├── file_1.h | ||
├── file_2.c | ||
├── some_dir | ||
│ ├── another_dir | ||
│ │ ├── file_3.h | ||
│ │ └── file_4.c | ||
would result in: | ||
|
||
.. code-block:: text | ||
doc_dir | ||
├── files.rst | ||
├── file_1_h.rst | ||
├── file_2_c.rst | ||
├── some_dir | ||
│ ├── some_dir.rst | ||
│ ├── another_dir | ||
│ │ ├── another_dir.rst | ||
│ │ ├── file_3_h.rst | ||
│ │ └── file_4_c.rst | ||
Where `a_project` was provided as the ``source_path`` and `doc_dir` was provided as the | ||
``--output-path``. ``files.rst`` is the root index or table of contents file. By | ||
default it only contains references to the other documentation files in the same | ||
directory and any index files in sub directories. | ||
|
||
``another_dir.rst`` is the index or table of contents file for the directory | ||
`another_dir` it will only contain references the files in that directory as well as | ||
any index files in subsequent sub directories. | ||
|
||
Templates | ||
--------- | ||
|
||
There are three jinja templates that are utilized for generating the documentation | ||
files. These can be overridden by passing a directory, via the ``--templatedir`` | ||
option, containing any of the templates to override. | ||
|
||
header.rst.jinja2 | ||
^^^^^^^^^^^^^^^^^ | ||
|
||
Controls the generation of files deemed to be header files, the ``--header-ext`` | ||
option. | ||
|
||
Will be passed 2 arguments: | ||
|
||
- ``filepath`` The relative path to the file. For ``file_3.h``, from the example | ||
above in :ref:`apidoc:Generated Documentation Files`, this would be | ||
``some_dir/another_dir/file_3.h`` | ||
- ``filename`` The name of the file, without relative directory. For ``file_3.h``, | ||
from the example above in :ref:`apidoc:Generated Documentation Files`, this would | ||
be ``file_3.h`` | ||
|
||
|
||
source.rst.jinja2 | ||
^^^^^^^^^^^^^^^^^ | ||
|
||
Controls the generation of files deemed to be source files, the ``--source-ext`` | ||
option. | ||
|
||
Will be passed 2 arguments: | ||
|
||
- ``filepath`` The relative path to the file. For ``file_4.c``, from the example | ||
above in :ref:`apidoc:Generated Documentation Files`, this would be | ||
``some_dir/another_dir/file_4.c`` | ||
- ``filename`` The name of the file, without relative directory. For ``file_4.c``, | ||
from the example above in :ref:`apidoc:Generated Documentation Files`, this would | ||
be ``file_4.c`` | ||
|
||
toc.rst.jinja2 | ||
^^^^^^^^^^^^^^ | ||
|
||
Controls the generation of index or table of contents files. | ||
|
||
Will be passed 3 arguments: | ||
|
||
- ``title`` The name of the index file without extension. | ||
- ``maxdepth`` The ``-d`` option. | ||
- ``doc_names`` The list of documentation files in the directory as well as those | ||
in subdirectories. | ||
|
||
.. _sphinx-apidoc: https://www.sphinx-doc.org/en/master/man/sphinx-apidoc.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
""" | ||
Provide apidoc like functionality for C projects. | ||
""" | ||
import argparse | ||
import os | ||
|
||
from pathlib import Path | ||
from typing import Sequence, Optional | ||
|
||
from sphinx.util.template import ReSTRenderer | ||
|
||
|
||
def get_parser() -> argparse.ArgumentParser: | ||
""" | ||
Gets the argument parser for this module | ||
Returns: | ||
argparse.ArgumentParser: Argument parser to be used with this module. | ||
""" | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"-o", | ||
"--output-path", | ||
help="Directory to place the output files. If it does not exist, it is " | ||
"created", | ||
) | ||
parser.add_argument( | ||
"-f", | ||
"--force", | ||
help="Force overwriting of any existing generated files", | ||
action="store_true", | ||
) | ||
parser.add_argument("source_path", help="Path to C source files to be documented") | ||
parser.add_argument( | ||
"-t", "--templatedir", help="Template directory for template files" | ||
) | ||
parser.add_argument( | ||
"--tocfile", | ||
help="Filename for the root table of contents file (default: %(default)s)", | ||
default="files", | ||
) | ||
parser.add_argument( | ||
"-d", | ||
dest="maxdepth", | ||
help="Maximum depth for the generated table of contents file(s). " | ||
"(default: %(default)s)", | ||
default=4, | ||
type=int, | ||
) | ||
parser.add_argument( | ||
"--header-ext", | ||
help='The extension(s) to use for header files (default: ["h"])', | ||
action="append", | ||
) | ||
parser.add_argument( | ||
"--source-ext", | ||
help='The extension(s) to use for source files (default: ["c"])', | ||
action="append", | ||
) | ||
return parser | ||
|
||
|
||
def render_doc_file( | ||
source_file: Path, doc_file: Path, template_name: str, user_template_dir: str | ||
) -> None: | ||
""" | ||
Renders a documentation file, `doc_file`, for the provided `source_file`. | ||
Args: | ||
source_file: The source file to be documented. | ||
doc_file: The resultant documentation file that will document `source_file`. | ||
template_name: The name of the template to use to populate `doc_file` with. | ||
user_template_dir: A directory to contain possible user overrides of | ||
`template_name`. | ||
""" | ||
doc_file.parent.mkdir(parents=True, exist_ok=True) | ||
context = {"filepath": "/".join(source_file.parts), "filename": source_file.name} | ||
template_dir = os.path.dirname(__file__) + "/templates" | ||
text = ReSTRenderer([user_template_dir, template_dir]).render( | ||
template_name, context | ||
) | ||
doc_file.write_text(text) | ||
|
||
|
||
def build_directory_docs( | ||
source_path: Path, output_path: Path, toc_name: str, args: argparse.Namespace | ||
) -> str: | ||
""" | ||
Recursively build the documentation for the `source_path`. Each file to be | ||
documented will be added to the table of contents in `toc_name`. Any sub | ||
directories which contain documented files will also be added to `toc_name`. | ||
Args: | ||
source_path: The path to the source directory to make documentation for. | ||
output_path: The destination path for the generated documentation files. | ||
toc_name: The name of this directories toc(table of contents) or index file. | ||
args: Arguments used for generating the documentation file. | ||
Returns: | ||
str: The reference to `toc_name` that can be used by parent documentation | ||
directories. | ||
""" | ||
doc_names = [] | ||
for entry in os.scandir(source_path): | ||
path = Path(entry) | ||
if entry.is_dir(): | ||
name = path.name | ||
dir_doc = build_directory_docs(path, output_path / name, name, args) | ||
if dir_doc: | ||
doc_names.append(dir_doc) | ||
if entry.is_file(): | ||
file_doc = create_file_documentation(path, output_path, args) | ||
if file_doc: | ||
doc_names.append(file_doc) | ||
|
||
if doc_names: | ||
return render_directory_index_file(output_path, toc_name, doc_names, args) | ||
return "" | ||
|
||
|
||
def create_file_documentation( | ||
source_file: Path, output_path: Path, args: argparse.Namespace | ||
) -> str: | ||
""" | ||
Attempts to create the file documentation for `source_file`. | ||
Args: | ||
source_file: The file to create documentation for. | ||
output_path: The directory to place the generated file documentation. | ||
args: Arguments used for generating the documentation file. | ||
Returns: | ||
str: The short name of the documentation file can be placed in a sibling | ||
index file. Empty if this file shouldn't be documented. | ||
""" | ||
name = source_file.name | ||
normalized_name = name.replace(".", "_") | ||
doc_name = f"{normalized_name}.rst" | ||
doc_file = output_path / doc_name | ||
if not args.force and doc_file.exists(): | ||
return normalized_name | ||
|
||
template = None | ||
|
||
if name.endswith(args.header_ext): | ||
template = "header.rst.jinja2" | ||
elif name.endswith(args.source_ext): | ||
template = "source.rst.jinja2" | ||
|
||
if not template: | ||
return "" | ||
|
||
relative_path = source_file.relative_to(args.source_path) | ||
render_doc_file(relative_path, doc_file, template, args.templatedir) | ||
return normalized_name | ||
|
||
|
||
def render_directory_index_file( | ||
output_path: Path, index_name: str, doc_names: Sequence, args: argparse.Namespace | ||
) -> str: | ||
""" | ||
Renders the template for the directory index file. | ||
Args: | ||
output_path: The location to render the directory index file in. | ||
index_name: The name of the directory index file | ||
doc_names: The list of files to provide in the director index file. | ||
args: Arguments used for generating the index file. | ||
Returns: | ||
str: The name of the index file to be placed in parent index files. | ||
""" | ||
index_file = output_path / f"{index_name}.rst" | ||
context = { | ||
"doc_names": doc_names, | ||
"maxdepth": args.maxdepth, | ||
"title": index_name, | ||
} | ||
template_dir = os.path.dirname(__file__) + "/templates" | ||
text = ReSTRenderer([args.templatedir, template_dir]).render( | ||
"toc.rst.jinja2", context | ||
) | ||
index_file.write_text(text) | ||
return f"{output_path.name}/{index_name}" | ||
|
||
|
||
def main(argv: Optional[Sequence] = None) -> int: | ||
""" | ||
The main entry point for this module. Will parse the provided arguments(`argv`) | ||
and generated the documentation files. | ||
Args: | ||
argv: The arguments to use for documentation generation. Use `--help` to see | ||
the full documentation. If no arguments provided then sys.argv will be | ||
used. | ||
Returns: | ||
int: 0 if success. Other than 0 on failure. | ||
""" | ||
parser = get_parser() | ||
args = parser.parse_args(argv) | ||
|
||
output_path = Path(args.output_path) | ||
source_path = Path(args.source_path) | ||
|
||
if not args.header_ext: | ||
args.header_ext = ["h"] | ||
args.header_ext = tuple(f".{ext}" for ext in args.header_ext) | ||
|
||
if not args.source_ext: | ||
args.source_ext = ["c"] | ||
args.source_ext = tuple(f".{ext}" for ext in args.source_ext) | ||
|
||
build_directory_docs(source_path, output_path, args.tocfile, args) | ||
return 0 | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{{ filename | heading }} | ||
|
||
.. autocmodule:: {{ filepath }} | ||
:members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{{ filename | heading }} | ||
|
||
.. autocmodule:: {{ filepath }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{{ title | heading }} | ||
|
||
.. toctree:: | ||
:maxdepth: {{ maxdepth }} | ||
{% for doc_name in doc_names %} | ||
{{ doc_name }} | ||
{%- endfor %} |
Oops, something went wrong.