mlc-doxygen-bin 1.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. mlc_doxygen_bin-1.17.0/MANIFEST.in +7 -0
  2. mlc_doxygen_bin-1.17.0/PKG-INFO +110 -0
  3. mlc_doxygen_bin-1.17.0/README.md +93 -0
  4. mlc_doxygen_bin-1.17.0/pyproject.toml +69 -0
  5. mlc_doxygen_bin-1.17.0/scripts/cibuildwheel_run.sh +20 -0
  6. mlc_doxygen_bin-1.17.0/scripts/copy_wheel.py +19 -0
  7. mlc_doxygen_bin-1.17.0/scripts/install_manylinux_build_tools.sh +50 -0
  8. mlc_doxygen_bin-1.17.0/scripts/prepare_doxygen_v1_17_0.py +326 -0
  9. mlc_doxygen_bin-1.17.0/scripts/smoke_test_mlc_doxygen_bin.py +71 -0
  10. mlc_doxygen_bin-1.17.0/scripts/wheel_macos_v1_17_0.sh +131 -0
  11. mlc_doxygen_bin-1.17.0/scripts/wheel_manylinux_2_28_v1_17_0.sh +60 -0
  12. mlc_doxygen_bin-1.17.0/scripts/wheel_windows_v1_17_0.ps1 +52 -0
  13. mlc_doxygen_bin-1.17.0/setup.cfg +4 -0
  14. mlc_doxygen_bin-1.17.0/setup.py +27 -0
  15. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin/__init__.py +42 -0
  16. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin/__main__.py +7 -0
  17. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin/_version.py +2 -0
  18. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin/cli.py +11 -0
  19. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin/py.typed +1 -0
  20. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin.egg-info/PKG-INFO +110 -0
  21. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin.egg-info/SOURCES.txt +23 -0
  22. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin.egg-info/dependency_links.txt +1 -0
  23. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin.egg-info/entry_points.txt +2 -0
  24. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin.egg-info/not-zip-safe +1 -0
  25. mlc_doxygen_bin-1.17.0/src/mlc_doxygen_bin.egg-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include setup.py
3
+ recursive-include scripts *.py *.sh *.ps1
4
+ recursive-include src/mlc_doxygen_bin *.py py.typed
5
+ prune src/mlc_doxygen_bin/bin
6
+ prune src/mlc_doxygen_bin/licenses
7
+ exclude src/mlc_doxygen_bin/*.json
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlc-doxygen-bin
3
+ Version: 1.17.0
4
+ Summary: Prebuilt Doxygen 1.17.0 command-line executable
5
+ Author-email: MLC AI <mlc.ai.dev@gmail.com>
6
+ License-Expression: GPL-2.0-only
7
+ Project-URL: Doxygen, https://github.com/doxygen/doxygen
8
+ Project-URL: Repository, https://github.com/mlc-ai/package
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Documentation
14
+ Classifier: Topic :: Software Development :: Documentation
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+
18
+ # mlc-doxygen-bin
19
+
20
+ `mlc-doxygen-bin` builds platform wheels containing the Doxygen 1.17.0 command-line
21
+ executable. It is intended for Python-driven build systems that want a pinned
22
+ Doxygen binary without relying on a system package manager.
23
+
24
+ The wheel installs a `doxygen` console script that delegates to the executable
25
+ stored inside the Python package.
26
+
27
+ ## Build Wheels
28
+
29
+ For Doxygen 1.17.0 on macOS:
30
+
31
+ ```bash
32
+ scripts/wheel_macos_v1_17_0.sh
33
+ ```
34
+
35
+ For Doxygen 1.17.0 on manylinux_2_28:
36
+
37
+ ```bash
38
+ scripts/wheel_manylinux_2_28_v1_17_0.sh
39
+ ```
40
+
41
+ For Doxygen 1.17.0 on Windows:
42
+
43
+ ```powershell
44
+ scripts\wheel_windows_v1_17_0.ps1
45
+ ```
46
+
47
+ The scripts run `cibuildwheel==3.3.1` through `uv`. During CIBW's
48
+ `before-build` phase, `scripts/prepare_doxygen_v1_17_0.py` downloads the pinned
49
+ upstream source archive, verifies its SHA-256 digest, builds the CLI executable,
50
+ and copies the install tree into `src/mlc_doxygen_bin`.
51
+
52
+ Build artifacts are written to `wheelhouse/`.
53
+
54
+ ## Platform And Architecture
55
+
56
+ The Linux and macOS scripts default to the host architecture. Override the
57
+ target architecture with:
58
+
59
+ ```bash
60
+ MLC_DOXYGEN_BIN_ARCH=aarch64 scripts/wheel_manylinux_2_28_v1_17_0.sh
61
+ MLC_DOXYGEN_BIN_ARCH=arm64 scripts/wheel_macos_v1_17_0.sh
62
+ ```
63
+
64
+ Supported values are:
65
+
66
+ - Linux: `x86_64`, `aarch64`
67
+ - macOS: `arm64`, `x86_64`
68
+ - Windows: `AMD64`
69
+
70
+ ## Build Dependencies
71
+
72
+ The build needs CMake, Ninja, Python, Flex, Bison, and a C++ compiler. The
73
+ manylinux script installs Bison from the manylinux distribution and builds a
74
+ pinned Flex 2.6.4 into `/usr/local`, because AlmaLinux 8's Flex 2.6.1 can fail
75
+ while generating Doxygen's large scanners.
76
+
77
+ On macOS, use Homebrew Flex and Bison if the Apple-provided versions are too
78
+ old:
79
+
80
+ ```bash
81
+ brew install flex bison
82
+ ```
83
+
84
+ The macOS script automatically prepends Homebrew's `flex` and `bison`
85
+ directories to `PATH` when Homebrew is available.
86
+
87
+ On Windows, install Visual Studio Build Tools and `winflexbison3`.
88
+
89
+ ## Runtime Scope
90
+
91
+ This package intentionally builds only the Doxygen CLI executable. It does not
92
+ bundle `doxywizard`, `doxyindexer`, `doxysearch.cgi`, libclang, Graphviz,
93
+ LaTeX, Ghostscript, or PlantUML. Those tools remain external runtime
94
+ dependencies for Doxygen features that need them.
95
+
96
+ ## Usage
97
+
98
+ ```bash
99
+ python -m pip install mlc-doxygen-bin==1.17.0
100
+ doxygen --version
101
+ ```
102
+
103
+ Programmatic path lookup is also available:
104
+
105
+ ```python
106
+ import mlc_doxygen_bin
107
+
108
+ print(mlc_doxygen_bin.get_executable_path())
109
+ print(mlc_doxygen_bin.metadata())
110
+ ```
@@ -0,0 +1,93 @@
1
+ # mlc-doxygen-bin
2
+
3
+ `mlc-doxygen-bin` builds platform wheels containing the Doxygen 1.17.0 command-line
4
+ executable. It is intended for Python-driven build systems that want a pinned
5
+ Doxygen binary without relying on a system package manager.
6
+
7
+ The wheel installs a `doxygen` console script that delegates to the executable
8
+ stored inside the Python package.
9
+
10
+ ## Build Wheels
11
+
12
+ For Doxygen 1.17.0 on macOS:
13
+
14
+ ```bash
15
+ scripts/wheel_macos_v1_17_0.sh
16
+ ```
17
+
18
+ For Doxygen 1.17.0 on manylinux_2_28:
19
+
20
+ ```bash
21
+ scripts/wheel_manylinux_2_28_v1_17_0.sh
22
+ ```
23
+
24
+ For Doxygen 1.17.0 on Windows:
25
+
26
+ ```powershell
27
+ scripts\wheel_windows_v1_17_0.ps1
28
+ ```
29
+
30
+ The scripts run `cibuildwheel==3.3.1` through `uv`. During CIBW's
31
+ `before-build` phase, `scripts/prepare_doxygen_v1_17_0.py` downloads the pinned
32
+ upstream source archive, verifies its SHA-256 digest, builds the CLI executable,
33
+ and copies the install tree into `src/mlc_doxygen_bin`.
34
+
35
+ Build artifacts are written to `wheelhouse/`.
36
+
37
+ ## Platform And Architecture
38
+
39
+ The Linux and macOS scripts default to the host architecture. Override the
40
+ target architecture with:
41
+
42
+ ```bash
43
+ MLC_DOXYGEN_BIN_ARCH=aarch64 scripts/wheel_manylinux_2_28_v1_17_0.sh
44
+ MLC_DOXYGEN_BIN_ARCH=arm64 scripts/wheel_macos_v1_17_0.sh
45
+ ```
46
+
47
+ Supported values are:
48
+
49
+ - Linux: `x86_64`, `aarch64`
50
+ - macOS: `arm64`, `x86_64`
51
+ - Windows: `AMD64`
52
+
53
+ ## Build Dependencies
54
+
55
+ The build needs CMake, Ninja, Python, Flex, Bison, and a C++ compiler. The
56
+ manylinux script installs Bison from the manylinux distribution and builds a
57
+ pinned Flex 2.6.4 into `/usr/local`, because AlmaLinux 8's Flex 2.6.1 can fail
58
+ while generating Doxygen's large scanners.
59
+
60
+ On macOS, use Homebrew Flex and Bison if the Apple-provided versions are too
61
+ old:
62
+
63
+ ```bash
64
+ brew install flex bison
65
+ ```
66
+
67
+ The macOS script automatically prepends Homebrew's `flex` and `bison`
68
+ directories to `PATH` when Homebrew is available.
69
+
70
+ On Windows, install Visual Studio Build Tools and `winflexbison3`.
71
+
72
+ ## Runtime Scope
73
+
74
+ This package intentionally builds only the Doxygen CLI executable. It does not
75
+ bundle `doxywizard`, `doxyindexer`, `doxysearch.cgi`, libclang, Graphviz,
76
+ LaTeX, Ghostscript, or PlantUML. Those tools remain external runtime
77
+ dependencies for Doxygen features that need them.
78
+
79
+ ## Usage
80
+
81
+ ```bash
82
+ python -m pip install mlc-doxygen-bin==1.17.0
83
+ doxygen --version
84
+ ```
85
+
86
+ Programmatic path lookup is also available:
87
+
88
+ ```python
89
+ import mlc_doxygen_bin
90
+
91
+ print(mlc_doxygen_bin.get_executable_path())
92
+ print(mlc_doxygen_bin.metadata())
93
+ ```
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=77",
4
+ "wheel>=0.43",
5
+ ]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "mlc-doxygen-bin"
10
+ version = "1.17.0"
11
+ description = "Prebuilt Doxygen 1.17.0 command-line executable"
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ license = "GPL-2.0-only"
15
+ authors = [{ name = "MLC AI", email = "mlc.ai.dev@gmail.com" }]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Documentation",
22
+ "Topic :: Software Development :: Documentation",
23
+ ]
24
+
25
+ [project.urls]
26
+ Doxygen = "https://github.com/doxygen/doxygen"
27
+ Repository = "https://github.com/mlc-ai/package"
28
+
29
+ [project.scripts]
30
+ doxygen = "mlc_doxygen_bin.cli:main"
31
+
32
+ [tool.setuptools]
33
+ package-dir = { "" = "src" }
34
+ include-package-data = true
35
+ zip-safe = false
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.setuptools.package-data]
41
+ mlc_doxygen_bin = [
42
+ "py.typed",
43
+ "*.json",
44
+ "bin/*",
45
+ "licenses/*",
46
+ ]
47
+
48
+ [tool.cibuildwheel]
49
+ # CIBW_BUILD selects the Python used to run the build. setup.py overrides the
50
+ # output wheel tag to py3-none because this package exposes a native executable,
51
+ # not a CPython ABI extension.
52
+ build = "cp311-*"
53
+ build-frontend = "build"
54
+ build-verbosity = 1
55
+ skip = "*-musllinux_*"
56
+ before-build = "python -m pip install cmake ninja && python scripts/prepare_doxygen_v1_17_0.py"
57
+ test-command = "python {project}/scripts/smoke_test_mlc_doxygen_bin.py"
58
+
59
+ [tool.cibuildwheel.linux]
60
+ manylinux-x86_64-image = "manylinux_2_28"
61
+ manylinux-aarch64-image = "manylinux_2_28"
62
+ before-all = "bash scripts/install_manylinux_build_tools.sh"
63
+ repair-wheel-command = "python scripts/copy_wheel.py {wheel} {dest_dir}"
64
+
65
+ [tool.cibuildwheel.macos]
66
+ repair-wheel-command = "python scripts/copy_wheel.py {wheel} {dest_dir}"
67
+
68
+ [tool.cibuildwheel.windows]
69
+ repair-wheel-command = "python scripts/copy_wheel.py {wheel} {dest_dir}"
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if [[ "$#" -lt 1 || "$#" -gt 3 ]]; then
5
+ echo "usage: cibuildwheel_run.sh <platform> [output-dir] [package-dir]" >&2
6
+ exit 1
7
+ fi
8
+
9
+ platform="$1"
10
+ output_dir="${2:-wheelhouse}"
11
+ package_dir="${3:-.}"
12
+
13
+ : "${UV:=uv}"
14
+ if ! command -v "${UV}" >/dev/null 2>&1; then
15
+ echo "uv executable not found: ${UV}" >&2
16
+ exit 1
17
+ fi
18
+
19
+ "${UV}" tool run --isolated --from "cibuildwheel==3.3.1" \
20
+ cibuildwheel --platform "${platform}" --output-dir "${output_dir}" "${package_dir}"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import shutil
5
+ import sys
6
+ from pathlib import Path
7
+
8
+
9
+ def main() -> None:
10
+ if len(sys.argv) != 3:
11
+ raise SystemExit("usage: copy_wheel.py <wheel> <dest-dir>")
12
+ wheel = Path(sys.argv[1]).resolve()
13
+ dest_dir = Path(sys.argv[2]).resolve()
14
+ dest_dir.mkdir(parents=True, exist_ok=True)
15
+ shutil.copy2(wheel, dest_dir / wheel.name)
16
+
17
+
18
+ if __name__ == "__main__":
19
+ main()
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ flex_version="2.6.4"
5
+ flex_url="https://github.com/westes/flex/releases/download/v${flex_version}/flex-${flex_version}.tar.gz"
6
+ flex_sha256="e87aae032bf07c26f85ac0ed3250998c37621d95f8bd748b31f15b33c45ee995"
7
+
8
+ : "${MLC_DOXYGEN_BIN_TOOLS_PREFIX:=/usr/local}"
9
+
10
+ if command -v yum >/dev/null 2>&1; then
11
+ yum install -y bison m4 make gcc gcc-c++ curl tar gzip
12
+ elif command -v dnf >/dev/null 2>&1; then
13
+ dnf install -y bison m4 make gcc gcc-c++ curl tar gzip
14
+ else
15
+ echo "Neither yum nor dnf is available; cannot install manylinux build tools" >&2
16
+ exit 1
17
+ fi
18
+
19
+ if [[ -x "${MLC_DOXYGEN_BIN_TOOLS_PREFIX}/bin/flex" ]]; then
20
+ installed_version="$("${MLC_DOXYGEN_BIN_TOOLS_PREFIX}/bin/flex" --version | sed -n '1s/.* \([0-9][0-9.]*\).*/\1/p')"
21
+ if [[ "${installed_version}" == "${flex_version}" ]]; then
22
+ "${MLC_DOXYGEN_BIN_TOOLS_PREFIX}/bin/flex" --version
23
+ bison --version | sed -n '1p'
24
+ exit 0
25
+ fi
26
+ fi
27
+
28
+ work_dir="$(mktemp -d)"
29
+ cleanup() {
30
+ rm -rf "${work_dir}"
31
+ }
32
+ trap cleanup EXIT
33
+
34
+ cd "${work_dir}"
35
+ curl -fsSL --connect-timeout 10 --retry 5 -o "flex-${flex_version}.tar.gz" "${flex_url}"
36
+ echo "${flex_sha256} flex-${flex_version}.tar.gz" | sha256sum -c -
37
+ tar -xzf "flex-${flex_version}.tar.gz"
38
+ cd "flex-${flex_version}"
39
+
40
+ # Flex 2.6.4 detects reallocarray on manylinux_2_28, but GCC 14 builds fail
41
+ # unless glibc exposes its declaration. Keep the feature macro explicit instead
42
+ # of suppressing implicit-function-declaration diagnostics.
43
+ export CFLAGS="${CFLAGS:-} -D_GNU_SOURCE"
44
+
45
+ ./configure --prefix="${MLC_DOXYGEN_BIN_TOOLS_PREFIX}" --disable-shared --enable-static
46
+ make -j"$(nproc)"
47
+ make install
48
+
49
+ "${MLC_DOXYGEN_BIN_TOOLS_PREFIX}/bin/flex" --version
50
+ bison --version | sed -n '1p'
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ import os
7
+ import platform
8
+ import shlex
9
+ import shutil
10
+ import subprocess
11
+ import sys
12
+ import tarfile
13
+ import urllib.request
14
+ from pathlib import Path
15
+
16
+ DOXYGEN_VERSION = "1.17.0"
17
+ DOXYGEN_TAG = "Release_1_17_0"
18
+ DOXYGEN_ARCHIVE_URL = (
19
+ "https://github.com/doxygen/doxygen/releases/download/"
20
+ f"{DOXYGEN_TAG}/doxygen-{DOXYGEN_VERSION}.src.tar.gz"
21
+ )
22
+ DOXYGEN_ARCHIVE_SHA256 = "fa4c3dd78785abc11ccc992bc9c01e7a8c3120fe14b8a8dfd7cefa7014530814"
23
+
24
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
25
+ DEFAULT_PACKAGE_DIR = PROJECT_ROOT / "src" / "mlc_doxygen_bin"
26
+
27
+
28
+ def run(cmd: list[str | Path], *, cwd: Path | None = None) -> None:
29
+ printable = " ".join(shlex.quote(str(part)) for part in cmd)
30
+ print(f"+ {printable}", flush=True)
31
+ subprocess.run([str(part) for part in cmd], cwd=cwd, check=True)
32
+
33
+
34
+ def sha256(path: Path) -> str:
35
+ digest = hashlib.sha256()
36
+ with path.open("rb") as source:
37
+ for chunk in iter(lambda: source.read(1024 * 1024), b""):
38
+ digest.update(chunk)
39
+ return digest.hexdigest()
40
+
41
+
42
+ def verify_archive(path: Path) -> bool:
43
+ if not path.is_file():
44
+ return False
45
+ actual = sha256(path)
46
+ if actual == DOXYGEN_ARCHIVE_SHA256:
47
+ return True
48
+ print(f"Discarding {path}: expected {DOXYGEN_ARCHIVE_SHA256}, got {actual}", flush=True)
49
+ path.unlink()
50
+ return False
51
+
52
+
53
+ def download_source_archive(work_root: Path) -> Path:
54
+ archive = work_root / f"doxygen-{DOXYGEN_VERSION}.src.tar.gz"
55
+ if verify_archive(archive):
56
+ return archive
57
+
58
+ tmp = archive.with_suffix(".tmp")
59
+ tmp.unlink(missing_ok=True)
60
+ print(f"Downloading Doxygen {DOXYGEN_VERSION} source from {DOXYGEN_ARCHIVE_URL}", flush=True)
61
+ with urllib.request.urlopen(DOXYGEN_ARCHIVE_URL, timeout=60) as response, tmp.open("wb") as dest:
62
+ shutil.copyfileobj(response, dest)
63
+ actual = sha256(tmp)
64
+ if actual != DOXYGEN_ARCHIVE_SHA256:
65
+ tmp.unlink(missing_ok=True)
66
+ raise RuntimeError(
67
+ "Doxygen source archive SHA-256 mismatch: "
68
+ f"expected {DOXYGEN_ARCHIVE_SHA256}, got {actual}"
69
+ )
70
+ tmp.replace(archive)
71
+ return archive
72
+
73
+
74
+ def safe_extract(archive: Path, source_root: Path) -> Path:
75
+ source_dir = source_root / f"doxygen-{DOXYGEN_VERSION}"
76
+ if (source_dir / "CMakeLists.txt").is_file():
77
+ return source_dir
78
+
79
+ extract_dir = source_root / "_extract"
80
+ if extract_dir.exists():
81
+ shutil.rmtree(extract_dir)
82
+ extract_dir.mkdir(parents=True, exist_ok=True)
83
+
84
+ base = extract_dir.resolve()
85
+ with tarfile.open(archive, "r:gz") as tar:
86
+ members = tar.getmembers()
87
+ for member in members:
88
+ target = (extract_dir / member.name).resolve()
89
+ if target != base and base not in target.parents:
90
+ raise RuntimeError(f"Refusing to extract path outside destination: {member.name}")
91
+ extract_kwargs = {"filter": "data"} if hasattr(tarfile, "data_filter") else {}
92
+ tar.extractall(extract_dir, **extract_kwargs)
93
+
94
+ roots = [path for path in extract_dir.iterdir() if path.is_dir()]
95
+ if len(roots) != 1:
96
+ raise RuntimeError(f"Expected one root directory in {archive}, found {roots}")
97
+
98
+ if source_dir.exists():
99
+ shutil.rmtree(source_dir)
100
+ shutil.move(str(roots[0]), source_dir)
101
+ shutil.rmtree(extract_dir)
102
+ return source_dir
103
+
104
+
105
+ def get_source_tree(work_root: Path) -> Path:
106
+ source_override = os.environ.get("MLC_DOXYGEN_BIN_SOURCE_DIR")
107
+ if source_override:
108
+ source_dir = Path(source_override).resolve()
109
+ if not (source_dir / "CMakeLists.txt").is_file():
110
+ raise RuntimeError(f"MLC_DOXYGEN_BIN_SOURCE_DIR is not a Doxygen source tree: {source_dir}")
111
+ return source_dir
112
+
113
+ archive = download_source_archive(work_root)
114
+ return safe_extract(archive, work_root / "src")
115
+
116
+
117
+ def find_program(env_name: str, candidates: tuple[str, ...]) -> str | None:
118
+ configured = os.environ.get(env_name)
119
+ if configured:
120
+ return configured
121
+ if platform.system() == "Darwin":
122
+ brew = shutil.which("brew")
123
+ if brew:
124
+ for candidate in candidates:
125
+ result = subprocess.run(
126
+ [brew, "--prefix", "--installed", candidate],
127
+ check=False,
128
+ text=True,
129
+ stdout=subprocess.PIPE,
130
+ stderr=subprocess.DEVNULL,
131
+ )
132
+ if result.returncode == 0:
133
+ path = Path(result.stdout.strip()) / "bin" / candidate
134
+ if path.is_file():
135
+ return str(path)
136
+ for candidate in candidates:
137
+ resolved = shutil.which(candidate)
138
+ if resolved:
139
+ return resolved
140
+ return None
141
+
142
+
143
+ def cmake_configure_command(source_dir: Path, build_dir: Path, stage_dir: Path) -> list[str | Path]:
144
+ build_type = os.environ.get("MLC_DOXYGEN_BIN_BUILD_TYPE", "Release")
145
+ cmd: list[str | Path] = [
146
+ "cmake",
147
+ "-S",
148
+ source_dir,
149
+ "-B",
150
+ build_dir,
151
+ ]
152
+
153
+ generator = os.environ.get("MLC_DOXYGEN_BIN_CMAKE_GENERATOR")
154
+ if generator:
155
+ cmd.extend(["-G", generator])
156
+ elif platform.system() != "Windows" and shutil.which("ninja"):
157
+ cmd.extend(["-G", "Ninja"])
158
+
159
+ cmd.extend(
160
+ [
161
+ f"-DCMAKE_BUILD_TYPE={build_type}",
162
+ f"-DCMAKE_INSTALL_PREFIX={stage_dir}",
163
+ "-Dbuild_wizard=OFF",
164
+ "-Dbuild_app=OFF",
165
+ "-Dbuild_parse=OFF",
166
+ "-Dbuild_search=OFF",
167
+ "-Dbuild_doc=OFF",
168
+ "-Dbuild_doc_chm=OFF",
169
+ "-Duse_libclang=OFF",
170
+ "-Duse_sys_spdlog=OFF",
171
+ "-Duse_sys_fmt=OFF",
172
+ "-Duse_sys_sqlite3=OFF",
173
+ ]
174
+ )
175
+
176
+ flex = find_program("FLEX_EXECUTABLE", ("flex", "win_flex", "win_flex.exe"))
177
+ if flex:
178
+ cmd.append(f"-DFLEX_EXECUTABLE={flex}")
179
+ bison = find_program("BISON_EXECUTABLE", ("bison", "win_bison", "win_bison.exe"))
180
+ if bison:
181
+ cmd.append(f"-DBISON_EXECUTABLE={bison}")
182
+
183
+ if platform.system() == "Darwin":
184
+ deployment_target = os.environ.get("MACOSX_DEPLOYMENT_TARGET")
185
+ if deployment_target:
186
+ cmd.append(f"-DMACOS_VERSION_MIN={deployment_target}")
187
+ cmd.append(f"-DCMAKE_OSX_DEPLOYMENT_TARGET={deployment_target}")
188
+
189
+ if platform.system() == "Windows":
190
+ cmd.append("-Dwin_static=ON")
191
+
192
+ extra_args = os.environ.get("MLC_DOXYGEN_BIN_EXTRA_CMAKE_ARGS")
193
+ if extra_args:
194
+ cmd.extend(shlex.split(extra_args))
195
+
196
+ return cmd
197
+
198
+
199
+ def executable_name() -> str:
200
+ return "doxygen.exe" if platform.system() == "Windows" else "doxygen"
201
+
202
+
203
+ def validate_stage(stage_dir: Path) -> Path:
204
+ executable = stage_dir / "bin" / executable_name()
205
+ if not executable.is_file():
206
+ raise RuntimeError(f"Doxygen install tree is missing executable: {executable}")
207
+ return executable
208
+
209
+
210
+ def clean_package_outputs(package_dir: Path) -> None:
211
+ for name in ("bin", "licenses"):
212
+ shutil.rmtree(package_dir / name, ignore_errors=True)
213
+ for name in ("doxygen.json",):
214
+ (package_dir / name).unlink(missing_ok=True)
215
+
216
+
217
+ def copy_runtime(stage_dir: Path, package_dir: Path) -> Path:
218
+ source_bin = stage_dir / "bin"
219
+ dest_bin = package_dir / "bin"
220
+ shutil.copytree(source_bin, dest_bin)
221
+ executable = dest_bin / executable_name()
222
+ if platform.system() != "Windows":
223
+ executable.chmod(executable.stat().st_mode | 0o755)
224
+ return executable
225
+
226
+
227
+ def rel(package_dir: Path, path: Path) -> str:
228
+ return path.relative_to(package_dir).as_posix()
229
+
230
+
231
+ def write_version_file(package_dir: Path) -> None:
232
+ (package_dir / "_version.py").write_text(
233
+ (
234
+ f'__version__ = "{DOXYGEN_VERSION}"\n'
235
+ f'DOXYGEN_TAG = "{DOXYGEN_TAG}"\n'
236
+ ),
237
+ encoding="utf-8",
238
+ )
239
+
240
+
241
+ def write_license(package_dir: Path, source_dir: Path) -> str:
242
+ licenses_dir = package_dir / "licenses"
243
+ licenses_dir.mkdir(parents=True, exist_ok=True)
244
+ source_license = source_dir / "LICENSE"
245
+ dest = licenses_dir / "LICENSE-Doxygen.txt"
246
+ if source_license.is_file():
247
+ shutil.copy2(source_license, dest)
248
+ else:
249
+ dest.write_text("See https://github.com/doxygen/doxygen/blob/master/LICENSE\n", encoding="utf-8")
250
+ return rel(package_dir, dest)
251
+
252
+
253
+ def write_metadata(package_dir: Path, executable: Path, source_dir: Path, license_path: str) -> None:
254
+ metadata = {
255
+ "name": "doxygen",
256
+ "doxygen_version": DOXYGEN_VERSION,
257
+ "doxygen_tag": DOXYGEN_TAG,
258
+ "source_repository": "https://github.com/doxygen/doxygen",
259
+ "source_archive_url": DOXYGEN_ARCHIVE_URL,
260
+ "source_archive_sha256": DOXYGEN_ARCHIVE_SHA256,
261
+ "source_dir": str(source_dir),
262
+ "package_version": DOXYGEN_VERSION,
263
+ "executable": rel(package_dir, executable),
264
+ "license": license_path,
265
+ "features": {
266
+ "cli": True,
267
+ "doxywizard": False,
268
+ "search_tools": False,
269
+ "libclang": False,
270
+ },
271
+ "platform": platform.platform(),
272
+ "python": sys.version.split()[0],
273
+ }
274
+ (package_dir / "doxygen.json").write_text(
275
+ json.dumps(metadata, indent=2, sort_keys=True) + "\n",
276
+ encoding="utf-8",
277
+ )
278
+
279
+
280
+ def build_and_install(source_dir: Path, work_root: Path) -> Path:
281
+ build_dir = work_root / "build"
282
+ stage_dir = work_root / "stage"
283
+ if build_dir.exists():
284
+ shutil.rmtree(build_dir)
285
+ if stage_dir.exists():
286
+ shutil.rmtree(stage_dir)
287
+
288
+ run(cmake_configure_command(source_dir, build_dir, stage_dir))
289
+
290
+ build_type = os.environ.get("MLC_DOXYGEN_BIN_BUILD_TYPE", "Release")
291
+ build_cmd: list[str | Path] = [
292
+ "cmake",
293
+ "--build",
294
+ build_dir,
295
+ "--config",
296
+ build_type,
297
+ "--target",
298
+ "install",
299
+ ]
300
+ parallel = os.environ.get("CMAKE_BUILD_PARALLEL_LEVEL")
301
+ if parallel:
302
+ build_cmd.extend(["--parallel", parallel])
303
+ run(build_cmd)
304
+ validate_stage(stage_dir)
305
+ return stage_dir
306
+
307
+
308
+ def main() -> None:
309
+ package_dir = Path(os.environ.get("MLC_DOXYGEN_BIN_PACKAGE_DIR", DEFAULT_PACKAGE_DIR)).resolve()
310
+ build_root = Path(
311
+ os.environ.get("MLC_DOXYGEN_BIN_BUILD_ROOT", PROJECT_ROOT / "build" / "doxygen-v1.17.0")
312
+ ).resolve()
313
+ package_dir.mkdir(parents=True, exist_ok=True)
314
+ build_root.mkdir(parents=True, exist_ok=True)
315
+
316
+ source_dir = get_source_tree(build_root)
317
+ clean_package_outputs(package_dir)
318
+ stage_dir = build_and_install(source_dir, build_root)
319
+ executable = copy_runtime(stage_dir, package_dir)
320
+ license_path = write_license(package_dir, source_dir)
321
+ write_version_file(package_dir)
322
+ write_metadata(package_dir, executable, source_dir, license_path)
323
+
324
+
325
+ if __name__ == "__main__":
326
+ main()
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ from pathlib import Path
8
+
9
+ import mlc_doxygen_bin
10
+
11
+
12
+ def run(cmd: list[str], *, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
13
+ print("+ " + " ".join(cmd), flush=True)
14
+ return subprocess.run(cmd, cwd=cwd, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
15
+
16
+
17
+ def write_minimal_project(root: Path) -> Path:
18
+ source = root / "src"
19
+ output = root / "docs"
20
+ source.mkdir()
21
+ (source / "main.cpp").write_text(
22
+ """
23
+ /// Adds two integers.
24
+ int add(int left, int right) {
25
+ return left + right;
26
+ }
27
+ """.lstrip(),
28
+ encoding="utf-8",
29
+ )
30
+ doxyfile = root / "Doxyfile"
31
+ doxyfile.write_text(
32
+ f"""
33
+ PROJECT_NAME = smoke
34
+ OUTPUT_DIRECTORY = {output}
35
+ INPUT = {source}
36
+ QUIET = YES
37
+ WARN_AS_ERROR = YES
38
+ GENERATE_HTML = YES
39
+ GENERATE_LATEX = NO
40
+ HAVE_DOT = NO
41
+ """.lstrip(),
42
+ encoding="utf-8",
43
+ )
44
+ return doxyfile
45
+
46
+
47
+ def main() -> None:
48
+ executable = mlc_doxygen_bin.get_executable_path()
49
+ direct = run([executable, "--version"])
50
+ wrapped = run([sys.executable, "-m", "mlc_doxygen_bin", "--version"])
51
+ expected = mlc_doxygen_bin.__version__
52
+ if direct.stdout.strip() != expected:
53
+ raise RuntimeError(f"Expected direct Doxygen version {expected}, got {direct.stdout.strip()!r}")
54
+ if wrapped.stdout.strip() != expected:
55
+ raise RuntimeError(f"Expected wrapped Doxygen version {expected}, got {wrapped.stdout.strip()!r}")
56
+
57
+ with tempfile.TemporaryDirectory() as tmp:
58
+ root = Path(tmp)
59
+ doxyfile = write_minimal_project(root)
60
+ run([sys.executable, "-m", "mlc_doxygen_bin", str(doxyfile)], cwd=root)
61
+ index = root / "docs" / "html" / "index.html"
62
+ if not index.is_file():
63
+ raise RuntimeError(f"Doxygen did not generate expected HTML index: {index}")
64
+
65
+
66
+ if __name__ == "__main__":
67
+ try:
68
+ main()
69
+ except Exception as err:
70
+ print(f"smoke test failed: {err}", file=sys.stderr)
71
+ raise
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "${project_dir}"
6
+
7
+ if [[ "$(uname -s)" != "Darwin" ]]; then
8
+ echo "wheel_macos_v1_17_0.sh must run on macOS" >&2
9
+ exit 1
10
+ fi
11
+
12
+ detect_python_org_version() {
13
+ pkgutil --pkgs \
14
+ | sed -n 's/^org\.python\.Python\.PythonFramework-\([0-9][0-9.]*\)$/\1/p' \
15
+ | sort -t. -k1,1n -k2,2n \
16
+ | tail -n 1
17
+ }
18
+
19
+ prepend_homebrew_tool() {
20
+ local formula="$1"
21
+ if command -v brew >/dev/null 2>&1; then
22
+ local prefix
23
+ if prefix="$(brew --prefix --installed "${formula}" 2>/dev/null)" && [[ -d "${prefix}/bin" ]]; then
24
+ PATH="${prefix}/bin:${PATH}"
25
+ fi
26
+ fi
27
+ }
28
+
29
+ prepend_homebrew_tool flex
30
+ prepend_homebrew_tool bison
31
+ export PATH
32
+
33
+ host_arch="$(uname -m)"
34
+ : "${MLC_DOXYGEN_BIN_ARCH:=${host_arch}}"
35
+ case "${MLC_DOXYGEN_BIN_ARCH}" in
36
+ arm64|x86_64) ;;
37
+ *)
38
+ echo "Unsupported macOS architecture: ${MLC_DOXYGEN_BIN_ARCH}" >&2
39
+ exit 1
40
+ ;;
41
+ esac
42
+
43
+ for tool in flex bison; do
44
+ if ! command -v "${tool}" >/dev/null 2>&1; then
45
+ echo "${tool} is required to build Doxygen. Install it with: brew install flex bison" >&2
46
+ exit 1
47
+ fi
48
+ done
49
+ flex_executable="$(command -v flex)"
50
+ bison_executable="$(command -v bison)"
51
+
52
+ check_tool_version() {
53
+ local tool="$1"
54
+ local executable="$2"
55
+ local minimum="$3"
56
+ local actual
57
+ actual="$("${executable}" --version | sed -n '1s/.* \([0-9][0-9.]*\).*/\1/p')"
58
+ if [[ -z "${actual}" ]]; then
59
+ echo "Could not determine ${tool} version" >&2
60
+ exit 1
61
+ fi
62
+ python3 - "${tool}" "${actual}" "${minimum}" <<'PY'
63
+ import re
64
+ import sys
65
+
66
+ tool, actual, minimum = sys.argv[1:]
67
+
68
+ def parts(version):
69
+ return tuple(int(part) for part in re.findall(r"\d+", version))
70
+
71
+ if parts(actual) < parts(minimum):
72
+ raise SystemExit(
73
+ f"{tool} {minimum} or newer is required to build Doxygen; found {actual}. "
74
+ "Install Homebrew flex and bison with: brew install flex bison"
75
+ )
76
+ PY
77
+ }
78
+
79
+ check_tool_version flex "${flex_executable}" 2.5.37
80
+ check_tool_version bison "${bison_executable}" 2.7
81
+
82
+ if command -v sysctl >/dev/null 2>&1; then
83
+ cpu_count="$(sysctl -n hw.ncpu)"
84
+ else
85
+ cpu_count="2"
86
+ fi
87
+
88
+ : "${MACOSX_DEPLOYMENT_TARGET:=13.0}"
89
+ : "${CMAKE_BUILD_PARALLEL_LEVEL:=${cpu_count}}"
90
+
91
+ platform_suffix="${MACOSX_DEPLOYMENT_TARGET//./_}_${MLC_DOXYGEN_BIN_ARCH}"
92
+ : "${MLC_DOXYGEN_BIN_PLAT_NAME:=macosx_${platform_suffix}}"
93
+
94
+ if [[ -z "${CIBW_BUILD:-}" ]]; then
95
+ python_org_version="$(detect_python_org_version)"
96
+ if [[ -z "${python_org_version}" ]]; then
97
+ cat >&2 <<'EOF'
98
+ No python.org CPython installation was found.
99
+
100
+ cibuildwheel on local macOS will not install CPython for you. Install one of
101
+ the official python.org packages, then rerun this script. The wheel produced by
102
+ this package is tagged py3-none, so the local build interpreter version only
103
+ needs to be supported by cibuildwheel.
104
+ EOF
105
+ exit 1
106
+ fi
107
+ python_tag="cp${python_org_version//./}"
108
+ export CIBW_BUILD="${python_tag}-macosx_${MLC_DOXYGEN_BIN_ARCH}"
109
+ fi
110
+ export CIBW_ARCHS_MACOS="${CIBW_ARCHS_MACOS:-${MLC_DOXYGEN_BIN_ARCH}}"
111
+ export CIBW_BUILD_VERBOSITY="${CIBW_BUILD_VERBOSITY:-1}"
112
+ export CIBW_ENVIRONMENT="${CIBW_ENVIRONMENT:-MLC_DOXYGEN_BIN_PLAT_NAME='${MLC_DOXYGEN_BIN_PLAT_NAME}' MACOSX_DEPLOYMENT_TARGET='${MACOSX_DEPLOYMENT_TARGET}' CMAKE_BUILD_PARALLEL_LEVEL='${CMAKE_BUILD_PARALLEL_LEVEL}' FLEX_EXECUTABLE='${flex_executable}' BISON_EXECUTABLE='${bison_executable}'}"
113
+
114
+ scripts/cibuildwheel_run.sh macos wheelhouse .
115
+
116
+ shopt -s nullglob
117
+ wheels=(wheelhouse/*-py3-none-"${MLC_DOXYGEN_BIN_PLAT_NAME}".whl)
118
+ if [[ "${#wheels[@]}" -eq 0 ]]; then
119
+ echo "No ${MLC_DOXYGEN_BIN_PLAT_NAME} wheels found in wheelhouse" >&2
120
+ exit 1
121
+ fi
122
+ for wheel in "${wheels[@]}"; do
123
+ filename="$(basename "${wheel}")"
124
+ case "${filename}" in
125
+ *-py3-none-*) ;;
126
+ *)
127
+ echo "Expected Python-agnostic py3-none wheel tag, got ${filename}" >&2
128
+ exit 1
129
+ ;;
130
+ esac
131
+ done
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "${project_dir}"
6
+
7
+ if [[ "$(uname -s)" != "Linux" ]]; then
8
+ echo "wheel_manylinux_2_28_v1_17_0.sh must run on Linux with Docker available" >&2
9
+ exit 1
10
+ fi
11
+
12
+ host_arch="$(uname -m)"
13
+ if [[ "${host_arch}" == "arm64" ]]; then
14
+ host_arch="aarch64"
15
+ fi
16
+
17
+ : "${MLC_DOXYGEN_BIN_ARCH:=${host_arch}}"
18
+ case "${MLC_DOXYGEN_BIN_ARCH}" in
19
+ x86_64|aarch64) ;;
20
+ *)
21
+ echo "Unsupported manylinux architecture: ${MLC_DOXYGEN_BIN_ARCH}" >&2
22
+ exit 1
23
+ ;;
24
+ esac
25
+
26
+ if command -v nproc >/dev/null 2>&1; then
27
+ cpu_count="$(nproc)"
28
+ else
29
+ cpu_count="2"
30
+ fi
31
+
32
+ : "${CMAKE_BUILD_PARALLEL_LEVEL:=${cpu_count}}"
33
+ : "${MLC_DOXYGEN_BIN_PLAT_NAME:=manylinux_2_28_${MLC_DOXYGEN_BIN_ARCH}}"
34
+ : "${MLC_DOXYGEN_BIN_TOOLS_PREFIX:=/usr/local}"
35
+
36
+ export CIBW_BUILD="${CIBW_BUILD:-cp311-manylinux_${MLC_DOXYGEN_BIN_ARCH}}"
37
+ export CIBW_ARCHS_LINUX="${CIBW_ARCHS_LINUX:-${MLC_DOXYGEN_BIN_ARCH}}"
38
+ export CIBW_MANYLINUX_X86_64_IMAGE="${CIBW_MANYLINUX_X86_64_IMAGE:-manylinux_2_28}"
39
+ export CIBW_MANYLINUX_AARCH64_IMAGE="${CIBW_MANYLINUX_AARCH64_IMAGE:-manylinux_2_28}"
40
+ export CIBW_BUILD_VERBOSITY="${CIBW_BUILD_VERBOSITY:-1}"
41
+ export CIBW_ENVIRONMENT="${CIBW_ENVIRONMENT:-MLC_DOXYGEN_BIN_PLAT_NAME='${MLC_DOXYGEN_BIN_PLAT_NAME}' CMAKE_BUILD_PARALLEL_LEVEL='${CMAKE_BUILD_PARALLEL_LEVEL}' MLC_DOXYGEN_BIN_TOOLS_PREFIX='${MLC_DOXYGEN_BIN_TOOLS_PREFIX}' FLEX_EXECUTABLE='${MLC_DOXYGEN_BIN_TOOLS_PREFIX}/bin/flex' BISON_EXECUTABLE='/usr/bin/bison'}"
42
+
43
+ scripts/cibuildwheel_run.sh linux wheelhouse .
44
+
45
+ shopt -s nullglob
46
+ wheels=(wheelhouse/*-py3-none-"${MLC_DOXYGEN_BIN_PLAT_NAME}".whl)
47
+ if [[ "${#wheels[@]}" -eq 0 ]]; then
48
+ echo "No ${MLC_DOXYGEN_BIN_PLAT_NAME} wheels found in wheelhouse" >&2
49
+ exit 1
50
+ fi
51
+ for wheel in "${wheels[@]}"; do
52
+ filename="$(basename "${wheel}")"
53
+ case "${filename}" in
54
+ *-py3-none-*) ;;
55
+ *)
56
+ echo "Expected Python-agnostic py3-none wheel tag, got ${filename}" >&2
57
+ exit 1
58
+ ;;
59
+ esac
60
+ done
@@ -0,0 +1,52 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $ProjectDir = Split-Path -Parent $PSScriptRoot
4
+ Set-Location $ProjectDir
5
+
6
+ if (-not $env:MLC_DOXYGEN_BIN_ARCH) {
7
+ $env:MLC_DOXYGEN_BIN_ARCH = "AMD64"
8
+ }
9
+ if ($env:MLC_DOXYGEN_BIN_ARCH -ne "AMD64") {
10
+ throw "Unsupported Windows architecture: $env:MLC_DOXYGEN_BIN_ARCH"
11
+ }
12
+
13
+ if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
14
+ throw "uv executable not found"
15
+ }
16
+ if (-not (Get-Command win_flex -ErrorAction SilentlyContinue) -and -not (Get-Command flex -ErrorAction SilentlyContinue)) {
17
+ throw "winflexbison3 is required to build Doxygen. Install it with: choco install winflexbison3"
18
+ }
19
+ if (-not (Get-Command win_bison -ErrorAction SilentlyContinue) -and -not (Get-Command bison -ErrorAction SilentlyContinue)) {
20
+ throw "winflexbison3 is required to build Doxygen. Install it with: choco install winflexbison3"
21
+ }
22
+
23
+ if (-not $env:CMAKE_BUILD_PARALLEL_LEVEL) {
24
+ $env:CMAKE_BUILD_PARALLEL_LEVEL = [string]$env:NUMBER_OF_PROCESSORS
25
+ }
26
+ if (-not $env:MLC_DOXYGEN_BIN_PLAT_NAME) {
27
+ $env:MLC_DOXYGEN_BIN_PLAT_NAME = "win_amd64"
28
+ }
29
+ if (-not $env:CIBW_BUILD) {
30
+ $env:CIBW_BUILD = "cp311-win_amd64"
31
+ }
32
+ if (-not $env:CIBW_ARCHS_WINDOWS) {
33
+ $env:CIBW_ARCHS_WINDOWS = "AMD64"
34
+ }
35
+ if (-not $env:CIBW_BUILD_VERBOSITY) {
36
+ $env:CIBW_BUILD_VERBOSITY = "1"
37
+ }
38
+ if (-not $env:CIBW_ENVIRONMENT) {
39
+ $env:CIBW_ENVIRONMENT = "MLC_DOXYGEN_BIN_PLAT_NAME='$env:MLC_DOXYGEN_BIN_PLAT_NAME' CMAKE_BUILD_PARALLEL_LEVEL='$env:CMAKE_BUILD_PARALLEL_LEVEL'"
40
+ }
41
+
42
+ uv tool run --isolated --from "cibuildwheel==3.3.1" cibuildwheel --platform windows --output-dir wheelhouse .
43
+
44
+ $Wheels = Get-ChildItem -Path wheelhouse -Filter "*-py3-none-$env:MLC_DOXYGEN_BIN_PLAT_NAME.whl"
45
+ if ($Wheels.Count -eq 0) {
46
+ throw "No $env:MLC_DOXYGEN_BIN_PLAT_NAME wheels found in wheelhouse"
47
+ }
48
+ foreach ($Wheel in $Wheels) {
49
+ if ($Wheel.Name -notlike "*-py3-none-*") {
50
+ throw "Expected Python-agnostic py3-none wheel tag, got $($Wheel.Name)"
51
+ }
52
+ }
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ from setuptools import setup
6
+ from setuptools.command.bdist_wheel import bdist_wheel as _bdist_wheel
7
+
8
+
9
+ class bdist_wheel(_bdist_wheel):
10
+ def finalize_options(self) -> None:
11
+ super().finalize_options()
12
+ self.root_is_pure = False
13
+ plat_name = os.environ.get("MLC_DOXYGEN_BIN_PLAT_NAME")
14
+ if plat_name:
15
+ self.plat_name = plat_name
16
+ self.plat_name_supplied = True
17
+
18
+ def get_tag(self) -> tuple[str, str, str]:
19
+ _python, _abi, plat = super().get_tag()
20
+ return "py3", "none", plat
21
+
22
+
23
+ setup(
24
+ cmdclass={
25
+ "bdist_wheel": bdist_wheel,
26
+ },
27
+ )
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import platform
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from ._version import DOXYGEN_TAG, __version__
9
+
10
+ __all__ = [
11
+ "__version__",
12
+ "DOXYGEN_TAG",
13
+ "get_bin_dir",
14
+ "get_executable_path",
15
+ "metadata",
16
+ ]
17
+
18
+
19
+ def _package_dir() -> Path:
20
+ return Path(__file__).resolve().parent
21
+
22
+
23
+ def get_bin_dir() -> str:
24
+ path = _package_dir() / "bin"
25
+ if not path.is_dir():
26
+ raise FileNotFoundError(f"Doxygen bin directory is missing: {path}")
27
+ return str(path)
28
+
29
+
30
+ def get_executable_path() -> str:
31
+ name = "doxygen.exe" if platform.system() == "Windows" else "doxygen"
32
+ path = Path(get_bin_dir()) / name
33
+ if not path.is_file():
34
+ raise FileNotFoundError(f"Doxygen executable is missing: {path}")
35
+ return str(path)
36
+
37
+
38
+ def metadata() -> dict[str, Any]:
39
+ path = _package_dir() / "doxygen.json"
40
+ if not path.is_file():
41
+ return {}
42
+ return json.loads(path.read_text(encoding="utf-8"))
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from .cli import main
4
+
5
+
6
+ if __name__ == "__main__":
7
+ raise SystemExit(main())
@@ -0,0 +1,2 @@
1
+ __version__ = "1.17.0"
2
+ DOXYGEN_TAG = "Release_1_17_0"
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ import sys
5
+
6
+ from . import get_executable_path
7
+
8
+
9
+ def main() -> int:
10
+ cmd = [get_executable_path(), *sys.argv[1:]]
11
+ return subprocess.run(cmd, check=False).returncode
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlc-doxygen-bin
3
+ Version: 1.17.0
4
+ Summary: Prebuilt Doxygen 1.17.0 command-line executable
5
+ Author-email: MLC AI <mlc.ai.dev@gmail.com>
6
+ License-Expression: GPL-2.0-only
7
+ Project-URL: Doxygen, https://github.com/doxygen/doxygen
8
+ Project-URL: Repository, https://github.com/mlc-ai/package
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Documentation
14
+ Classifier: Topic :: Software Development :: Documentation
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+
18
+ # mlc-doxygen-bin
19
+
20
+ `mlc-doxygen-bin` builds platform wheels containing the Doxygen 1.17.0 command-line
21
+ executable. It is intended for Python-driven build systems that want a pinned
22
+ Doxygen binary without relying on a system package manager.
23
+
24
+ The wheel installs a `doxygen` console script that delegates to the executable
25
+ stored inside the Python package.
26
+
27
+ ## Build Wheels
28
+
29
+ For Doxygen 1.17.0 on macOS:
30
+
31
+ ```bash
32
+ scripts/wheel_macos_v1_17_0.sh
33
+ ```
34
+
35
+ For Doxygen 1.17.0 on manylinux_2_28:
36
+
37
+ ```bash
38
+ scripts/wheel_manylinux_2_28_v1_17_0.sh
39
+ ```
40
+
41
+ For Doxygen 1.17.0 on Windows:
42
+
43
+ ```powershell
44
+ scripts\wheel_windows_v1_17_0.ps1
45
+ ```
46
+
47
+ The scripts run `cibuildwheel==3.3.1` through `uv`. During CIBW's
48
+ `before-build` phase, `scripts/prepare_doxygen_v1_17_0.py` downloads the pinned
49
+ upstream source archive, verifies its SHA-256 digest, builds the CLI executable,
50
+ and copies the install tree into `src/mlc_doxygen_bin`.
51
+
52
+ Build artifacts are written to `wheelhouse/`.
53
+
54
+ ## Platform And Architecture
55
+
56
+ The Linux and macOS scripts default to the host architecture. Override the
57
+ target architecture with:
58
+
59
+ ```bash
60
+ MLC_DOXYGEN_BIN_ARCH=aarch64 scripts/wheel_manylinux_2_28_v1_17_0.sh
61
+ MLC_DOXYGEN_BIN_ARCH=arm64 scripts/wheel_macos_v1_17_0.sh
62
+ ```
63
+
64
+ Supported values are:
65
+
66
+ - Linux: `x86_64`, `aarch64`
67
+ - macOS: `arm64`, `x86_64`
68
+ - Windows: `AMD64`
69
+
70
+ ## Build Dependencies
71
+
72
+ The build needs CMake, Ninja, Python, Flex, Bison, and a C++ compiler. The
73
+ manylinux script installs Bison from the manylinux distribution and builds a
74
+ pinned Flex 2.6.4 into `/usr/local`, because AlmaLinux 8's Flex 2.6.1 can fail
75
+ while generating Doxygen's large scanners.
76
+
77
+ On macOS, use Homebrew Flex and Bison if the Apple-provided versions are too
78
+ old:
79
+
80
+ ```bash
81
+ brew install flex bison
82
+ ```
83
+
84
+ The macOS script automatically prepends Homebrew's `flex` and `bison`
85
+ directories to `PATH` when Homebrew is available.
86
+
87
+ On Windows, install Visual Studio Build Tools and `winflexbison3`.
88
+
89
+ ## Runtime Scope
90
+
91
+ This package intentionally builds only the Doxygen CLI executable. It does not
92
+ bundle `doxywizard`, `doxyindexer`, `doxysearch.cgi`, libclang, Graphviz,
93
+ LaTeX, Ghostscript, or PlantUML. Those tools remain external runtime
94
+ dependencies for Doxygen features that need them.
95
+
96
+ ## Usage
97
+
98
+ ```bash
99
+ python -m pip install mlc-doxygen-bin==1.17.0
100
+ doxygen --version
101
+ ```
102
+
103
+ Programmatic path lookup is also available:
104
+
105
+ ```python
106
+ import mlc_doxygen_bin
107
+
108
+ print(mlc_doxygen_bin.get_executable_path())
109
+ print(mlc_doxygen_bin.metadata())
110
+ ```
@@ -0,0 +1,23 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ scripts/cibuildwheel_run.sh
6
+ scripts/copy_wheel.py
7
+ scripts/install_manylinux_build_tools.sh
8
+ scripts/prepare_doxygen_v1_17_0.py
9
+ scripts/smoke_test_mlc_doxygen_bin.py
10
+ scripts/wheel_macos_v1_17_0.sh
11
+ scripts/wheel_manylinux_2_28_v1_17_0.sh
12
+ scripts/wheel_windows_v1_17_0.ps1
13
+ src/mlc_doxygen_bin/__init__.py
14
+ src/mlc_doxygen_bin/__main__.py
15
+ src/mlc_doxygen_bin/_version.py
16
+ src/mlc_doxygen_bin/cli.py
17
+ src/mlc_doxygen_bin/py.typed
18
+ src/mlc_doxygen_bin.egg-info/PKG-INFO
19
+ src/mlc_doxygen_bin.egg-info/SOURCES.txt
20
+ src/mlc_doxygen_bin.egg-info/dependency_links.txt
21
+ src/mlc_doxygen_bin.egg-info/entry_points.txt
22
+ src/mlc_doxygen_bin.egg-info/not-zip-safe
23
+ src/mlc_doxygen_bin.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ doxygen = mlc_doxygen_bin.cli:main
@@ -0,0 +1 @@
1
+ mlc_doxygen_bin