Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Doxyfile configurations and update Doxygen plugin #98

Merged
merged 4 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/mkdoxy-pytest-repo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: MkDoxy Pytest MkDoxy with all supported Python versions

on: [push, pull_request]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest] # windows-latest, macos-latest requires doxygen to be installed manually
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
run: python -c "import sys; print(sys.version)"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
sudo apt-get install doxygen
- name: Run Pytest
run: |
pytest tests/
2 changes: 1 addition & 1 deletion .github/workflows/mkdoxy-test-demos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest] # windows-latest, macos-latest requires doxygen to be installed manually
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mkdoxy-test-repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest] # windows-latest, macos-latest requires doxygen to be installed manually
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
3 changes: 3 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 75 additions & 16 deletions .idea/workspace.xml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions demo-projects/animal/Doxyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DOXYFILE_ENCODING = UTF-8
GENERATE_XML = YES
RECURSIVE = YES
EXAMPLE_PATH = examples
SHOW_NAMESPACES = YES
GENERATE_HTML = NO
GENERATE_LATEX = YES
18 changes: 16 additions & 2 deletions docs/usage/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
## Disabling the plugin
You can use the `enabled` option to optionally disable this plugin. A possible use case is local development where you might want faster build times.

```yaml
```yaml hl_lines="3"
plugins:
- mkdoxy:
enabled: !ENV [ENABLE_MKDOXY, True]
Expand All @@ -29,9 +29,23 @@ If `doxygen-bin-path` is not found, the plugin will raise DoxygenBinPathNotValid

- addad by [thb-sb](https://github.com/thb-sb)

```yaml
```yaml hl_lines="3"
plugins:
- mkdoxy:
doxygen-bin-path: /path/to/doxygen
...
```

## Configure custom Doxygen configuration file
If you want to use a standard Doxygen configuration file, you can specify the path to the file using the `doxygen-config` option in the plugin configuration.

```yaml hl_lines="6"
plugins:
- mkdoxy:
projects:
myProjectCpp:
src-dirs: ...
doxy-cfg-file: path/to/Doxyfile # relative path to the Doxygen configuration file (relative to the mkdocs.yml file)
doxy-cfg: # standard doxygen configuration (key: value)
FILE_PATTERNS: ... # other configuration options - merge (this will override the configuration from the Doxyfile)
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ plugins:
src-dirs: mkdoxy
full-doc: True
# template-dir: templates-custom
doxy-cfg-file: demo-projects/animal/Doxyfile
doxy-cfg:
FILE_PATTERNS: "*.py"
EXAMPLE_PATH: ""
Expand Down
132 changes: 104 additions & 28 deletions mkdoxy/doxyrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class DoxygenRun:
"""! Class for running Doxygen.
@details This class is used to run Doxygen and parse the XML output.
@detailss This class is used to run Doxygen and parse the XML output.
"""

def __init__(
Expand All @@ -21,6 +21,7 @@ def __init__(
doxygenSource: str,
tempDoxyFolder: str,
doxyCfgNew,
doxyConfigFile: Optional[str] = None,
runPath: Optional[str] = None,
):
"""! Constructor.
Expand All @@ -36,10 +37,11 @@ def __init__(
- GENERATE_HTML: NO
- GENERATE_LATEX: NO

@details
@detailss
@param doxygenBinPath: (str) Path to the Doxygen binary.
@param doxygenSource: (str) Source files for Doxygen.
@param tempDoxyFolder: (str) Temporary folder for Doxygen.
@param doxyConfigFile: (str) Path to a Doxygen config file.
@param doxyCfgNew: (dict) New Doxygen config options that will be added to the default config (new options will overwrite default options)
""" # noqa: E501

Expand All @@ -53,28 +55,68 @@ def __init__(
self.doxygenBinPath: str = doxygenBinPath
self.doxygenSource: str = doxygenSource
self.tempDoxyFolder: str = tempDoxyFolder
self.doxyCfgNew: dict = doxyCfgNew
self.doxyConfigFile: Optional[str] = doxyConfigFile
self.hashFileName: str = "hashChanges.yaml"
self.hashFilePath: PurePath = PurePath.joinpath(Path(self.tempDoxyFolder), Path(self.hashFileName))
self.runPath: Optional[str] = runPath
self.doxyCfg: dict = self.setDoxyCfg(doxyCfgNew)

self.doxyCfg: dict = {
"INPUT": self.doxygenSource,
"OUTPUT_DIRECTORY": self.tempDoxyFolder,
"DOXYFILE_ENCODING": "UTF-8",
"GENERATE_XML": "YES",
"RECURSIVE": "YES",
"SHOW_NAMESPACES": "YES",
"GENERATE_HTML": "NO",
"GENERATE_LATEX": "NO",
}
def setDoxyCfg(self, doxyCfgNew: dict) -> dict:
"""! Set the Doxygen configuration.
@details If a custom Doxygen config file is provided, it will be used. Otherwise, default options will be used.
@details Order of application of parameters:
@details 1. Custom Doxygen config file
@details 2. If not provided, default options - in documentation
@details 3. New Doxygen config options from mkdocs.yml
@details 3. Overwrite INPUT and OUTPUT_DIRECTORY with the provided values for correct plugin operation.

self.doxyCfg.update(self.doxyCfgNew)
self.doxyCfgStr: str = self.dox_dict2str(self.doxyCfg)
@details Overwrite options description:
@details - INPUT: <doxygenSource>
@details - OUTPUT_DIRECTORY: <tempDoxyFolder>

@details Default Doxygen config options:
@details - DOXYFILE_ENCODING: UTF-8
@details - GENERATE_XML: YES
@details - RECURSIVE: YES
@details - EXAMPLE_PATH: examples
@details - SHOW_NAMESPACES: YES
@details - GENERATE_HTML: NO
@details - GENERATE_LATEX: NO
@param doxyCfgNew: (dict) New Doxygen config options that will be
added to the default config (new options will overwrite default options)
@return: (dict) Doxygen configuration.
"""
doxyCfg = {}

if self.doxyConfigFile is not None and self.doxyConfigFile != "":
try:
with open(self.doxyConfigFile, "r") as file:
doxyCfg.update(self.str2dox_dict(file.read()))
except FileNotFoundError as e:
raise DoxygenCustomConfigNotFound(
f"Custom Doxygen config file not found: {self.doxyConfigFile}\n"
f"Make sure the path is correct."
f"Look at https://mkdoxy.kubaandrysek.cz/usage/advanced/#configure-custom-doxygen-configuration-file."
) from e
else:
doxyCfg = {
"DOXYFILE_ENCODING": "UTF-8",
"GENERATE_XML": "YES",
"RECURSIVE": "YES",
"EXAMPLE_PATH": "examples",
"SHOW_NAMESPACES": "YES",
"GENERATE_HTML": "NO",
"GENERATE_LATEX": "NO",
}

doxyCfg.update(doxyCfgNew)
doxyCfg["INPUT"] = self.doxygenSource
doxyCfg["OUTPUT_DIRECTORY"] = self.tempDoxyFolder
return doxyCfg

def is_doxygen_valid_path(self, doxygen_bin_path: str) -> bool:
"""! Check if the Doxygen binary path is valid.
@details Accepts a full path or just 'doxygen' if it exists in the system's PATH.
@detailss Accepts a full path or just 'doxygen' if it exists in the system's PATH.
@param doxygen_bin_path: (str) The path to the Doxygen binary or just 'doxygen'.
@return: (bool) True if the Doxygen binary path is valid, False otherwise.
"""
Expand All @@ -89,7 +131,7 @@ def is_doxygen_valid_path(self, doxygen_bin_path: str) -> bool:
# Source of dox_dict2str: https://xdress-fabio.readthedocs.io/en/latest/_modules/xdress/doxygen.html#XDressPlugin
def dox_dict2str(self, dox_dict: dict) -> str:
"""! Convert a dictionary to a string that can be written to a doxygen config file.
@details
@detailss
@param dox_dict: (dict) Dictionary to convert.
@return: (str) String that can be written to a doxygen config file.
"""
Expand All @@ -108,17 +150,43 @@ def dox_dict2str(self, dox_dict: dict) -> str:
# Don't need an empty line at the end
return s.strip()

def str2dox_dict(self, dox_str: str) -> dict:
"""! Convert a string from a doxygen config file to a dictionary.
@detailss
@param dox_str: (str) String from a doxygen config file.
@return: (dict) Dictionary.
"""
dox_dict = {}
try:
for line in dox_str.split("\n"):
if line.strip() == "":
continue
key, value = line.split(" = ")
if value == "YES":
dox_dict[key] = True
elif value == "NO":
dox_dict[key] = False
else:
dox_dict[key] = value
except ValueError as e:
raise DoxygenCustomConfigNotValid(
f"Invalid custom Doxygen config file: {self.doxyConfigFile}\n"
f"Make sure the file is in standard Doxygen format."
f"Look at https://mkdoxy.kubaandrysek.cz/usage/advanced/."
) from e
return dox_dict

def hasChanged(self):
"""! Check if the source files have changed since the last run.
@details
@detailss
@return: (bool) True if the source files have changed since the last run.
"""

def heshWrite(filename: str, hash: str):
def hashWrite(filename: PurePath, hash: str):
with open(filename, "w") as file:
file.write(hash)

def hashRead(filename: str) -> str:
def hashRead(filename: PurePath) -> str:
with open(filename, "r") as file:
return str(file.read())

Expand All @@ -128,7 +196,7 @@ def hashRead(filename: str) -> str:
for path in Path(src).rglob("*.*"):
# # Code from https://stackoverflow.com/a/22058673/15411117
# # BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536 # lets read stuff in 64kb chunks!
BUF_SIZE = 65536 # let's read stuff in 64kb chunks!
if path.is_file():
with open(path, "rb") as f:
while True:
Expand All @@ -138,18 +206,18 @@ def hashRead(filename: str) -> str:
sha1.update(data)
# print(f"{path}: {sha1.hexdigest()}")

hahsNew = sha1.hexdigest()
hashNew = sha1.hexdigest()
if Path(self.hashFilePath).is_file():
hashOld = hashRead(self.hashFilePath)
if hahsNew == hashOld:
if hashNew == hashOld:
return False

heshWrite(self.hashFilePath, hahsNew)
hashWrite(self.hashFilePath, hashNew)
return True

def run(self):
"""! Run Doxygen with the current configuration using the Popen class.
@details
@detailss
"""
doxyBuilder = Popen(
[self.doxygenBinPath, "-"],
Expand All @@ -158,13 +226,13 @@ def run(self):
stderr=PIPE,
cwd=self.runPath,
)
(doxyBuilder.communicate(self.doxyCfgStr.encode("utf-8"))[0].decode().strip())
(doxyBuilder.communicate(self.dox_dict2str(self.doxyCfg).encode("utf-8"))[0].decode().strip())
# log.info(self.destinationDir)
# log.info(stdout_data)

def checkAndRun(self):
"""! Check if the source files have changed since the last run and run Doxygen if they have.
@details
@detailss
@return: (bool) True if Doxygen was run.
"""
if self.hasChanged():
Expand All @@ -175,7 +243,7 @@ def checkAndRun(self):

def getOutputFolder(self) -> PurePath:
"""! Get the path to the XML output folder.
@details
@detailss
@return: (PurePath) Path to the XML output folder.
"""
return Path.joinpath(Path(self.tempDoxyFolder), Path("xml"))
Expand All @@ -184,3 +252,11 @@ def getOutputFolder(self) -> PurePath:
# not valid path exception
class DoxygenBinPathNotValid(Exception):
pass


class DoxygenCustomConfigNotFound(Exception):
pass


class DoxygenCustomConfigNotValid(Exception):
pass
11 changes: 7 additions & 4 deletions mkdoxy/generatorBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,13 @@ def error(
):
"""! Render an error page.
@details
@param title (str): Title of the error page (default: "")
@param message (str): Message of the error page (default: "")
@param language (str): Programming language of the error page (default: "")
@return (str): Rendered error page.
@param config (dict): Config for the template.
@param title (str): Title of the error.
@param description (str): Description of the error.
@param code_header (str): Header of the code (default: "")
@param code (str): Code (default: "")
@param code_language (str): Language of the code (default: "")
@param snippet_code (str): Snippet code (default: "")
"""
if config is None:
config = {}
Expand Down
2 changes: 2 additions & 0 deletions mkdoxy/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class MkDoxy(BasePlugin):
("debug", config_options.Type(bool, default=False)),
# ('ignore-errors', config_options.Type(bool, default=False)),
("doxy-cfg", config_options.Type(dict, default={}, required=False)),
("doxy-cfg-file", config_options.Type(str, default="", required=False)),
("template-dir", config_options.Type(str, default="", required=False)),
)

Expand Down Expand Up @@ -119,6 +120,7 @@ def tempDir(siteDir: str, tempDir: str, projectName: str) -> str:
project_data.get("src-dirs"),
tempDirApi,
project_data.get("doxy-cfg", {}),
project_data.get("doxy-cfg-file", ""),
runPath,
)
if doxygenRun.checkAndRun():
Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ def requirements():
install_requires=["mkdocs"],
extras_require={
"dev": [
"mkdocs-material==9.1.18",
"mkdocs-material~=9.1.18",
"Jinja2~=3.1.2",
"mkdocs-open-in-new-tab~=1.0.2",
"pathlib~=1.0.1",
"path~=16.7.1",
"isort~=5.12.0",
"pytest~=6.2.5",
"pre-commit~=3.7.0",
],
},
classifiers=[
Expand Down
Loading
Loading