Skip to content

Commit

Permalink
Merge pull request #98 from JakubAndrysek/doxygen-config-file
Browse files Browse the repository at this point in the history
  • Loading branch information
JakubAndrysek committed Apr 2, 2024
2 parents 9189b40 + 0f1199a commit 7a65af2
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 53 deletions.
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

0 comments on commit 7a65af2

Please sign in to comment.