| | |
| | """ |
| | Script to automatically generate Sphinx documentation for all modules and build the HTML website. |
| | """ |
| | import importlib.util |
| | import os |
| | import subprocess |
| | import sys |
| |
|
| |
|
| | def check_and_install_dependencies(): |
| | """Check for required dependencies and install them if missing.""" |
| | required_packages = [ |
| | "sphinx", |
| | "sphinx-rtd-theme", |
| | "sphinxcontrib-napoleon", |
| | "sphinxcontrib-mermaid", |
| | "sphinx-autodoc-typehints", |
| | ] |
| |
|
| | missing_packages = [] |
| |
|
| | for package in required_packages: |
| | |
| | module_name = package.replace("-", "_") |
| |
|
| | |
| | if importlib.util.find_spec(module_name) is None: |
| | missing_packages.append(package) |
| |
|
| | |
| | if missing_packages: |
| | print(f"Installing missing dependencies: {', '.join(missing_packages)}") |
| | subprocess.check_call( |
| | [sys.executable, "-m", "pip", "install"] + missing_packages |
| | ) |
| | print("Dependencies installed successfully") |
| | else: |
| | print("All required dependencies are already installed") |
| |
|
| |
|
| | def create_makefile(docs_dir): |
| | """Create a Makefile for Sphinx documentation if it doesn't exist.""" |
| | makefile_path = os.path.join(docs_dir, "Makefile") |
| |
|
| | if os.path.exists(makefile_path): |
| | print(f"Makefile already exists at {makefile_path}") |
| | return |
| |
|
| | print(f"Creating Makefile at {makefile_path}") |
| |
|
| | makefile_content = """# Minimal makefile for Sphinx documentation |
| | |
| | # You can set these variables from the command line, and also |
| | # from the environment for the first two. |
| | SPHINXOPTS ?= |
| | SPHINXBUILD ?= sphinx-build |
| | SOURCEDIR = source |
| | BUILDDIR = build |
| | |
| | # Put it first so that "make" without argument is like "make help". |
| | help: |
| | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(SPHINXFLAGS) |
| | |
| | .PHONY: help Makefile |
| | |
| | # Catch-all target: route all unknown targets to Sphinx using the new |
| | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). |
| | %: Makefile |
| | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(SPHINXFLAGS) |
| | """ |
| |
|
| | with open(makefile_path, "w") as f: |
| | f.write(makefile_content) |
| |
|
| | print("Makefile created successfully") |
| |
|
| |
|
| | def create_make_bat(docs_dir): |
| | """Create a make.bat file for Windows if it doesn't exist.""" |
| | make_bat_path = os.path.join(docs_dir, "make.bat") |
| |
|
| | if os.path.exists(make_bat_path): |
| | print(f"make.bat already exists at {make_bat_path}") |
| | return |
| |
|
| | print(f"Creating make.bat at {make_bat_path}") |
| |
|
| | make_bat_content = """@ECHO OFF |
| | |
| | pushd %~dp0 |
| | |
| | REM Command file for Sphinx documentation |
| | |
| | if "%SPHINXBUILD%" == "" ( |
| | set SPHINXBUILD=sphinx-build |
| | ) |
| | set SOURCEDIR=source |
| | set BUILDDIR=build |
| | |
| | %SPHINXBUILD% >NUL 2>NUL |
| | if errorlevel 9009 ( |
| | echo. |
| | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx |
| | echo.installed, then set the SPHINXBUILD environment variable to point |
| | echo.to the full path of the 'sphinx-build' executable. Alternatively you |
| | echo.may add the Sphinx directory to PATH. |
| | echo. |
| | echo.If you don't have Sphinx installed, grab it from |
| | echo.https://www.sphinx-doc.org/ |
| | exit /b 1 |
| | ) |
| | |
| | if "%1" == "" goto help |
| | |
| | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% |
| | goto end |
| | |
| | :help |
| | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% |
| | |
| | :end |
| | popd |
| | """ |
| |
|
| | with open(make_bat_path, "w") as f: |
| | f.write(make_bat_content) |
| |
|
| | print("make.bat created successfully") |
| |
|
| |
|
| | def main(): |
| | |
| | print("=== Checking dependencies ===") |
| | check_and_install_dependencies() |
| |
|
| | |
| | script_dir = os.path.dirname(os.path.abspath(__file__)) |
| |
|
| | |
| | project_root = os.path.dirname(script_dir) |
| |
|
| | |
| | source_dir = os.path.join(project_root, "src") |
| |
|
| | |
| | docs_source_dir = os.path.join(script_dir, "source") |
| |
|
| | |
| | print(f"Script directory: {script_dir}") |
| | print(f"Project root: {project_root}") |
| | print(f"Source directory: {source_dir}") |
| | print(f"Docs source directory: {docs_source_dir}") |
| |
|
| | |
| | if not os.path.exists(source_dir): |
| | print(f"Error: Source directory {source_dir} does not exist!") |
| | sys.exit(1) |
| |
|
| | |
| | if not os.path.exists(docs_source_dir): |
| | print(f"Creating docs source directory: {docs_source_dir}") |
| | os.makedirs(docs_source_dir) |
| |
|
| | |
| | print("\n=== Generating API documentation ===") |
| | cmd = [ |
| | "sphinx-apidoc", |
| | "-f", |
| | "-e", |
| | "-M", |
| | "-o", |
| | docs_source_dir, |
| | source_dir, |
| | ] |
| |
|
| | print(f"Running command: {' '.join(cmd)}") |
| | result = subprocess.run(cmd, capture_output=True, text=True) |
| |
|
| | |
| | print("STDOUT:") |
| | print(result.stdout) |
| |
|
| | print("STDERR:") |
| | print(result.stderr) |
| |
|
| | if result.returncode != 0: |
| | print(f"Error: sphinx-apidoc failed with return code {result.returncode}") |
| | sys.exit(1) |
| |
|
| | |
| | print("\nFiles in docs/source directory:") |
| | for file in sorted(os.listdir(docs_source_dir)): |
| | print(f" {file}") |
| |
|
| | print("\nDocumentation source files generated successfully!") |
| |
|
| | |
| | create_makefile(script_dir) |
| | create_make_bat(script_dir) |
| |
|
| | |
| | print("\n=== Building HTML documentation ===") |
| |
|
| | |
| | if os.name == "nt": |
| | build_cmd = ["make.bat", "html"] |
| | else: |
| | build_cmd = ["make", "html"] |
| |
|
| | |
| | os.chdir(script_dir) |
| |
|
| | print(f"Running command: {' '.join(build_cmd)}") |
| | build_result = subprocess.run(build_cmd, capture_output=True, text=True) |
| |
|
| | |
| | print("STDOUT:") |
| | print(build_result.stdout) |
| |
|
| | print("STDERR:") |
| | print(build_result.stderr) |
| |
|
| | if build_result.returncode != 0: |
| | print(f"Error: HTML build failed with return code {build_result.returncode}") |
| | sys.exit(1) |
| |
|
| | |
| | html_dir = os.path.join(script_dir, "build", "html") |
| | index_path = os.path.join(html_dir, "index.html") |
| |
|
| | if os.path.exists(index_path): |
| | print(f"\nHTML documentation built successfully!") |
| | print(f"You can view it by opening: {index_path}") |
| |
|
| | |
| | try: |
| | import webbrowser |
| |
|
| | print("\nAttempting to open documentation in your default browser...") |
| | webbrowser.open(f"file://{index_path}") |
| | except Exception as e: |
| | print(f"Could not open browser automatically: {e}") |
| | else: |
| | print(f"\nWarning: HTML index file not found at {index_path}") |
| |
|
| |
|
| | if __name__ == "__main__": |
| | main() |
| |
|