Skip to content

Merge branch 'master' of https://github.com/Tiogaplanet/MPU_Pro_Mini_lib #36

Merge branch 'master' of https://github.com/Tiogaplanet/MPU_Pro_Mini_lib

Merge branch 'master' of https://github.com/Tiogaplanet/MPU_Pro_Mini_lib #36

Workflow file for this run

name: Deploy Doxygen API Docs to GitHub Wiki
on:
push:
branches:
- master
- minor-edits
- preflight-checks
jobs:
deploy-wiki:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Doxygen
run: |
sudo apt-get update
sudo apt-get install -y doxygen
- name: Generate Doxygen XML
run: |
# Generate default Doxyfile if not present
if [ ! -f Doxyfile ]; then
doxygen -g
fi
# Configure Doxyfile settings for XML generation and exclude internal namespaces
sed -i 's/GENERATE_XML = NO/GENERATE_XML = YES/' Doxyfile
sed -i 's/XML_OUTPUT = xml/XML_OUTPUT = xml/' Doxyfile
sed -i 's/GENERATE_HTML = YES/GENERATE_HTML = NO/' Doxyfile
sed -i 's/GENERATE_LATEX = YES/GENERATE_LATEX = NO/' Doxyfile
sed -i 's/RECURSIVE = NO/RECURSIVE = YES/' Doxyfile
sed -i 's/EXCLUDE_SYMBOLS =/EXCLUDE_SYMBOLS = mip_detail::*/' Doxyfile
# Run Doxygen
doxygen Doxyfile
- name: Convert Doxygen XML to Sectioned Markdown
run: |
mkdir -p wiki_markdown
python3 - << 'EOF'
import xml.etree.ElementTree as ET
import os
xml_dir = 'xml'
out_dir = 'wiki_markdown'
index_path = os.path.join(xml_dir, 'index.xml')
if not os.path.exists(index_path):
print("No index.xml found.")
exit(0)
def get_text(element):
""" Recursively extracts clean text from XML elements """
if element is None:
return ""
return "".join(element.itertext()).strip()
def sanitize_cell(text):
""" Strips newlines, collapses spaces, and escapes pipe characters for Markdown table cells """
if not text:
return ""
text = text.replace('\r', ' ').replace('\n', ' ').replace('|', '\\|')
return " ".join(text.split())
tree = ET.parse(index_path)
root = tree.getroot()
class_pages = []
enum_pages = []
defines_list = []
seen_enums = set()
seen_defines = set()
# ---------------------------------------------------------------------------
# 1. Parse Classes, Structs, Enums, and Preprocessor Defines
# ---------------------------------------------------------------------------
for compound in root.findall('compound'):
kind = compound.get('kind')
name = compound.find('name').text
refid = compound.get('refid')
# Classes & Structs
if kind in ['class', 'struct'] and not name.startswith('mip_detail::'):
clean_name = name.replace(':', '_').replace('/', '_').replace('.', '_')
doc_filename = f"{clean_name}.md"
class_pages.append((name, clean_name))
page = [f"# Class `{name}`\n\n"]
detail_path = os.path.join(xml_dir, f"{refid}.xml")
if os.path.exists(detail_path):
d_tree = ET.parse(detail_path)
d_root = d_tree.getroot()
comp_def = d_root.find('compounddef')
# --- Class @brief Description ---
brief = get_text(comp_def.find('briefdescription'))
if brief:
page.append(f"{brief}\n\n")
# --- Class @details Description ---
deta = get_text(comp_def.find('detaileddescription'))
if deta:
page.append(f"## Detailed Description\n\n{deta}\n\n")
# --- Categorize Public Members ---
members = comp_def.findall('.//memberdef')
public_members = [m for m in members if m.get('prot') == 'public']
constructors = []
functions = []
variables = []
for m in public_members:
m_kind = m.get('kind')
m_name = get_text(m.find('name'))
if m_kind == 'function':
if m_name == name or m_name == f"~{name}":
constructors.append(m)
else:
functions.append(m)
elif m_kind == 'variable':
variables.append(m)
elif m_kind == 'enum' and not m_name.startswith('mip_detail::') and m_name not in seen_enums:
seen_enums.add(m_name)
enum_pages.append((m_name, m_name, m))
# --- Table of Contents / Section Jump Links ---
toc = ["## Table of Contents\n\n"]
if constructors:
toc.append("- [Constructors & Destructors](#constructors--destructors)\n")
if functions:
toc.append("- [Public Functions](#public-functions)\n")
if variables:
toc.append("- [Public Variables & Constants](#public-variables)\n")
if constructors or functions or variables:
page.append("".join(toc) + "\n---\n\n")
# Helper function to format member elements
def format_member(member):
m_name = get_text(member.find('name'))
m_type = get_text(member.find('type'))
m_args = get_text(member.find('argsstring'))
m_brief = get_text(member.find('briefdescription'))
m_deta_el = member.find('detaileddescription')
sig = f"`{m_type} {m_name}{m_args}`" if m_type else f"`{m_name}{m_args}`"
m_block = [f"### `{m_name}`\n\n", f"**Signature:** {sig}\n\n"]
if m_brief:
m_block.append(f"**Summary:** {m_brief}\n\n")
if m_deta_el is not None:
para_text = []
for para in m_deta_el.findall('para'):
p_txt = get_text(para)
if p_txt and not para.find('parameterlist') and not para.find('simplesect'):
para_text.append(p_txt)
if para_text:
m_block.append("**Details:**\n\n" + "\n\n".join(para_text) + "\n\n")
param_items = member.findall('.//parameteritem')
if param_items:
m_block.append("**Parameters:**\n\n")
m_block.append("| Parameter | Description |\n|:---|:---|\n")
for p_item in param_items:
p_name = sanitize_cell(get_text(p_item.find('.//parametername')))
p_desc = sanitize_cell(get_text(p_item.find('.//parameterdescription')))
m_block.append(f"| `{p_name}` | {p_desc} |\n")
m_block.append("\n")
returns = member.findall(".//simplesect[@kind='return']")
if returns:
ret_desc = get_text(returns[0])
if ret_desc:
m_block.append(f"**Returns:** {ret_desc}\n\n")
m_block.append("---\n\n")
return "".join(m_block)
# --- Section 1: Constructors & Destructors ---
if constructors:
page.append("## Constructors & Destructors\n\n")
for c in constructors:
page.append(format_member(c))
# --- Section 2: Public Functions ---
if functions:
page.append("## Public Functions\n\n")
for f in functions:
page.append(format_member(f))
# --- Section 3: Public Variables & Constants ---
if variables:
page.append("## Public Variables\n\n")
for v in variables:
page.append(format_member(v))
with open(os.path.join(out_dir, doc_filename), 'w', encoding='utf-8') as f:
f.write("".join(page))
# Header File Compounds (contain global enums and #defines)
elif kind == 'file':
detail_path = os.path.join(xml_dir, f"{refid}.xml")
if os.path.exists(detail_path):
f_tree = ET.parse(detail_path)
f_root = f_tree.getroot()
# Enums
for enum_def in f_root.findall('.//memberdef[@kind="enum"]'):
e_name = get_text(enum_def.find('name'))
if e_name and not e_name.startswith('mip_detail::') and e_name not in seen_enums:
seen_enums.add(e_name)
enum_pages.append((e_name, e_name, enum_def))
# Preprocessor Defines (#define)
for def_member in f_root.findall('.//memberdef[@kind="define"]'):
d_name = sanitize_cell(get_text(def_member.find('name')))
d_init = sanitize_cell(get_text(def_member.find('initializer')))
d_brief = sanitize_cell(get_text(def_member.find('briefdescription')))
d_deta = sanitize_cell(get_text(def_member.find('detaileddescription')))
desc = d_brief if d_brief else d_deta
if d_name and not d_name.startswith('mip_detail::') and d_name not in seen_defines:
seen_defines.add(d_name)
defines_list.append((d_name, d_init, desc))
# ---------------------------------------------------------------------------
# 2. Format & Write Dedicated Enum Pages
# ---------------------------------------------------------------------------
for orig_name, clean_name, enum_def in enum_pages:
doc_filename = f"{clean_name}.md"
brief = get_text(enum_def.find('briefdescription'))
deta_el = enum_def.find('detaileddescription')
page = [f"# Enum `{orig_name}`\n\n"]
if brief:
page.append(f"{brief}\n\n")
if deta_el is not None:
para_text = []
for para in deta_el.findall('para'):
p_txt = get_text(para)
if p_txt and not para.find('parameterlist') and not para.find('simplesect'):
para_text.append(p_txt)
if para_text:
page.append("## Detailed Description\n\n" + "\n\n".join(para_text) + "\n\n")
enum_values = enum_def.findall('enumvalue')
if enum_values:
page.append("## Enumerator Values\n\n")
page.append("| Enumerator | Value | Description |\n|:---|:---|:---|\n")
for ev in enum_values:
ev_name = sanitize_cell(get_text(ev.find('name')))
ev_init = sanitize_cell(get_text(ev.find('initializer')))
ev_brief = get_text(ev.find('briefdescription'))
ev_deta = get_text(ev.find('detaileddescription'))
desc = sanitize_cell(ev_brief if ev_brief else ev_deta)
val_str = f"`{ev_init}`" if ev_init else ""
page.append(f"| `{ev_name}` | {val_str} | {desc} |\n")
page.append("\n")
with open(os.path.join(out_dir, doc_filename), 'w', encoding='utf-8') as f:
f.write("".join(page))
# ---------------------------------------------------------------------------
# 3. Write Constants-and-Defines.md
# ---------------------------------------------------------------------------
if defines_list:
with open(os.path.join(out_dir, 'Constants-and-Defines.md'), 'w', encoding='utf-8') as f:
f.write("# Constants and Defines\n\nGlobal preprocessor macros and defines:\n\n")
f.write("| Macro / Define | Value | Description |\n|:---|:---|:---|\n")
for d_name, d_init, d_desc in sorted(defines_list, key=lambda x: x[0]):
val_str = f"`{d_init}`" if d_init else ""
f.write(f"| `{d_name}` | {val_str} | {d_desc} |\n")
f.write("\n")
# ---------------------------------------------------------------------------
# 4. Generate API-Reference.md Index
# ---------------------------------------------------------------------------
with open(os.path.join(out_dir, 'API-Reference.md'), 'w', encoding='utf-8') as f:
f.write("# API Reference Index\n\nList of all public library classes, enumerations, and constants:\n\n")
f.write("## Classes\n\n")
for orig_name, clean_name in sorted(class_pages, key=lambda x: x[0]):
f.write(f"- **[{orig_name}]({clean_name})**\n")
if enum_pages:
f.write("\n## Enumerations\n\n")
for orig_name, clean_name, _ in sorted(enum_pages, key=lambda x: x[0]):
f.write(f"- **[{orig_name}]({clean_name})**\n")
if defines_list:
f.write("\n## Constants and Defines\n\n")
f.write("- **[Constants and Defines](Constants-and-Defines)**\n")
# ---------------------------------------------------------------------------
# 5. Generate _Sidebar.md Navigation
# ---------------------------------------------------------------------------
with open(os.path.join(out_dir, '_Sidebar.md'), 'w', encoding='utf-8') as f:
f.write("### Navigation\n\n")
f.write("- [Home](Home)\n")
f.write("- [API Reference](API-Reference)\n\n")
f.write("#### Classes\n\n")
for orig_name, clean_name in sorted(class_pages, key=lambda x: x[0]):
f.write(f"- [{orig_name}]({clean_name})\n")
if enum_pages:
f.write("\n#### Enumerations\n\n")
for orig_name, clean_name, _ in sorted(enum_pages, key=lambda x: x[0]):
f.write(f"- [{orig_name}]({clean_name})\n")
if defines_list:
f.write("\n#### Constants\n\n")
f.write("- [Constants and Defines](Constants-and-Defines)\n")
print("Generated Class, Enum, and Constants Markdown pages, API-Reference.md, and _Sidebar.md")
EOF
- name: Checkout Wiki Repository
uses: actions/checkout@v4
with:
repository: ${{ github.repository }}.wiki
path: wiki_repo
token: ${{ secrets.GITHUB_TOKEN }}
- name: Purge Old Pages and Deploy Fresh Wiki
run: |
cd wiki_repo
# PURGE STEP: Remove generated pages, but keep Home.md, _Footer.md, and .git
find . -maxdepth 1 ! -name '.' ! -name '..' ! -name '.git' \
! -name 'Home.md' ! -name '_Footer.md' -exec rm -rf {} +
cd ..
# Copy freshly generated markdown files into wiki_repo without overwriting
# custom Home.md or _Footer.md
rsync -av --exclude='Home.md' --exclude='_Footer.md' wiki_markdown/ wiki_repo/
# Initialize Home.md if it does not exist in the wiki repository
if [ ! -f wiki_repo/Home.md ]; then
echo "# Welcome to the Library Wiki" > wiki_repo/Home.md
echo "See the [API Reference](API-Reference) for detailed class documentation." >> wiki_repo/Home.md
fi
cd wiki_repo
git config user.name "Tiogaplanet"
git config user.email "tiogaplanet@users.noreply.github.com"
# Stage all deletions and new files
git add -A
if ! git diff-index --quiet HEAD; then
git commit -m "Fresh deployment of API documentation from Doxygen"
git push
else
echo "No changes to commit to Wiki."
fi