1# Zephyr documentation build configuration file.
2# Reference: https://www.sphinx-doc.org/en/master/usage/configuration.html
3
4import os
5import re
6import sys
7import textwrap
8from pathlib import Path
9
10ZEPHYR_BASE = Path(__file__).resolve().parents[1]
11ZEPHYR_BUILD = Path(os.environ.get("OUTPUT_DIR")).resolve()
12
13# Add the '_extensions' directory to sys.path, to enable finding Sphinx
14# extensions within.
15sys.path.insert(0, str(ZEPHYR_BASE / "doc" / "_extensions"))
16
17# Add the '_scripts' directory to sys.path, to enable finding utility
18# modules.
19sys.path.insert(0, str(ZEPHYR_BASE / "doc" / "_scripts"))
20
21# Add the directory which contains the runners package as well,
22# for autodoc directives on runners.xyz.
23sys.path.insert(0, str(ZEPHYR_BASE / "scripts" / "west_commands"))
24
25# Add the directory which contains the pytest-twister-pytest
26sys.path.insert(0, str(ZEPHYR_BASE / "scripts" / "pylib" / "pytest-twister-harness" / "src"))
27
28import redirects  # noqa: E402
29
30try:
31    import west as west_found
32except ImportError:
33    west_found = False
34
35# -- Project --------------------------------------------------------------
36
37project = "Zephyr Project"
38copyright = "2015-2025 Zephyr Project members and individual contributors"
39author = "The Zephyr Project Contributors"
40
41# parse version from 'VERSION' file
42with open(ZEPHYR_BASE / "VERSION") as f:
43    m = re.match(
44        (
45            r"^VERSION_MAJOR\s*=\s*(\d+)$\n"
46            + r"^VERSION_MINOR\s*=\s*(\d+)$\n"
47            + r"^PATCHLEVEL\s*=\s*(\d+)$\n"
48            + r"^VERSION_TWEAK\s*=\s*\d+$\n"
49            + r"^EXTRAVERSION\s*=\s*(.*)$"
50        ),
51        f.read(),
52        re.MULTILINE,
53    )
54
55    if not m:
56        sys.stderr.write("Warning: Could not extract kernel version\n")
57        version = "Unknown"
58    else:
59        major, minor, patch, extra = m.groups(1)
60        version = ".".join((major, minor, patch))
61        if extra:
62            version += "-" + extra
63
64release = version
65
66# parse SDK version from 'SDK_VERSION' file
67with open(ZEPHYR_BASE / "SDK_VERSION") as f:
68    sdk_version = f.read().strip()
69
70# -- General configuration ------------------------------------------------
71
72extensions = [
73    "sphinx_rtd_theme",
74    "sphinx.ext.todo",
75    "sphinx.ext.extlinks",
76    "sphinx.ext.autodoc",
77    "sphinx.ext.graphviz",
78    "sphinxcontrib.jquery",
79    "sphinxcontrib.programoutput",
80    "zephyr.application",
81    "zephyr.html_redirects",
82    "zephyr.kconfig",
83    "zephyr.dtcompatible-role",
84    "zephyr.link-roles",
85    "sphinx_tabs.tabs",
86    "sphinx_sitemap",
87    "zephyr.doxyrunner",
88    "zephyr.doxybridge",
89    "zephyr.doxytooltip",
90    "zephyr.gh_utils",
91    "zephyr.manifest_projects_table",
92    "notfound.extension",
93    "sphinx_copybutton",
94    "sphinx_togglebutton",
95    "zephyr.external_content",
96    "zephyr.domain",
97    "zephyr.api_overview",
98]
99
100# Only use image conversion when it is really needed, e.g. LaTeX build.
101# Ensure "sphinxcontrib.rsvgconverter" is added before "sphinx.ext.imgconverter"
102# as it's better at converting SVG with extended features (like the ones from
103# draw.io) to PDF format).
104if tags.has("convertimages"):  # pylint: disable=undefined-variable  # noqa: F821
105    extensions.append("sphinxcontrib.rsvgconverter")
106    extensions.append("sphinx.ext.imgconverter")
107
108templates_path = ["_templates"]
109
110exclude_patterns = ["_build"]
111
112if not west_found:
113    exclude_patterns.append("**/*west-apis*")
114else:
115    exclude_patterns.append("**/*west-not-found*")
116
117# Ensure only one of the two top-level indexes ever gets included.
118# This is a workaround for Sphinx issuing INFO notices about being referenced in
119# multiple toctrees.
120if tags.has("convertimages"):  # pylint: disable=undefined-variable  # noqa: F821
121    exclude_patterns.append("index.rst")
122    root_doc = "index-tex"
123else:
124    exclude_patterns.append("index-tex.rst")
125
126pygments_style = "sphinx"
127highlight_language = "none"
128
129todo_include_todos = False
130
131nitpick_ignore = [
132    # ignore C standard identifiers (they are not defined in Zephyr docs)
133    ("c:identifier", "FILE"),
134    ("c:identifier", "int8_t"),
135    ("c:identifier", "int16_t"),
136    ("c:identifier", "int32_t"),
137    ("c:identifier", "int64_t"),
138    ("c:identifier", "intptr_t"),
139    ("c:identifier", "off_t"),
140    ("c:identifier", "size_t"),
141    ("c:identifier", "ssize_t"),
142    ("c:identifier", "time_t"),
143    ("c:identifier", "uint8_t"),
144    ("c:identifier", "uint16_t"),
145    ("c:identifier", "uint32_t"),
146    ("c:identifier", "uint64_t"),
147    ("c:identifier", "uintptr_t"),
148    ("c:identifier", "va_list"),
149]
150
151SDK_URL_BASE="https://github.com/zephyrproject-rtos/sdk-ng/releases/download"
152
153rst_epilog = f"""
154.. include:: /substitutions.txt
155
156.. |zephyr-version| replace:: ``{version}``
157.. |zephyr-version-ltrim| unicode:: {version}
158   :ltrim:
159.. |sdk-version-literal| replace:: ``{sdk_version}``
160.. |sdk-version-trim| unicode:: {sdk_version}
161   :trim:
162.. |sdk-version-ltrim| unicode:: {sdk_version}
163   :ltrim:
164.. _Zephyr SDK bundle: https://github.com/zephyrproject-rtos/sdk-ng/releases/tag/v{sdk_version}
165.. |sdk-url-linux| replace::
166   `{SDK_URL_BASE}/v{sdk_version}/zephyr-sdk-{sdk_version}_linux-x86_64.tar.xz`
167.. |sdk-url-linux-sha| replace::
168   `{SDK_URL_BASE}/v{sdk_version}/sha256.sum`
169.. |sdk-url-macos| replace::
170   `{SDK_URL_BASE}/v{sdk_version}/zephyr-sdk-{sdk_version}_macos-x86_64.tar.xz`
171.. |sdk-url-macos-sha| replace::
172   `{SDK_URL_BASE}/v{sdk_version}/sha256.sum`
173.. |sdk-url-windows| replace::
174   `{SDK_URL_BASE}/v{sdk_version}/zephyr-sdk-{sdk_version}_windows-x86_64.7z`
175"""
176
177# -- Options for HTML output ----------------------------------------------
178
179html_theme = "sphinx_rtd_theme"
180html_theme_options = {
181    "logo_only": True,
182    "prev_next_buttons_location": None,
183    "navigation_depth": 5,
184}
185html_baseurl = "https://docs.zephyrproject.org/latest/"
186html_title = "Zephyr Project Documentation"
187html_logo = str(ZEPHYR_BASE / "doc" / "_static" / "images" / "logo.svg")
188html_favicon = str(ZEPHYR_BASE / "doc" / "_static" / "images" / "favicon.png")
189html_static_path = [str(ZEPHYR_BASE / "doc" / "_static")]
190html_last_updated_fmt = "%b %d, %Y"
191html_domain_indices = False
192html_split_index = True
193html_show_sourcelink = False
194html_show_sphinx = False
195html_search_scorer = str(ZEPHYR_BASE / "doc" / "_static" / "js" / "scorer.js")
196html_additional_pages = {
197    "gsearch": "gsearch.html"
198}
199
200is_release = tags.has("release")  # pylint: disable=undefined-variable  # noqa: F821
201reference_prefix = ""
202if tags.has("publish"):  # pylint: disable=undefined-variable  # noqa: F821
203    reference_prefix = f"/{version}" if is_release else "/latest"
204docs_title = "Docs / {}".format(version if is_release else "Latest")
205html_context = {
206    "show_license": True,
207    "docs_title": docs_title,
208    "is_release": is_release,
209    "current_version": version,
210    "versions": (
211        ("latest", "/"),
212        ("4.3.0", "/4.3.0/"),
213        ("4.2.0", "/4.2.0/"),
214        ("3.7.0 (LTS)", "/3.7.0/"),
215    ),
216    "display_gh_links": True,
217    "reference_links": {
218        "API": f"{reference_prefix}/doxygen/html/index.html",
219        "Kconfig Options": f"{reference_prefix}/kconfig.html",
220        "Devicetree Bindings": f"{reference_prefix}/build/dts/api/bindings.html",
221        "West Projects": f"{reference_prefix}/develop/manifest/index.html",
222        "Glossary": f"{reference_prefix}/glossary.html",
223    },
224    # Set google_searchengine_id to your Search Engine ID to replace built-in search
225    # engine with Google's Programmable Search Engine.
226    # See https://programmablesearchengine.google.com/ for details.
227    "google_searchengine_id": "746031aa0d56d4912",
228}
229
230# -- Options for LaTeX output ---------------------------------------------
231
232latex_elements = {
233    "papersize": "a4paper",
234    "maketitle": (ZEPHYR_BASE / "doc" / "_static" / "latex" / "title.tex").read_text(),
235    "preamble": (ZEPHYR_BASE / "doc" / "_static" / "latex" / "preamble.tex").read_text(),
236    "makeindex": r"\usepackage[columns=1]{idxlayout}\makeindex",
237    "fontpkg": textwrap.dedent(r"""
238                                    \usepackage{noto}
239                                    \usepackage{inconsolata-nerd-font}
240                                    \usepackage[T1]{fontenc}
241                                """),
242    "sphinxsetup": ",".join(
243        (
244            # NOTE: colors match those found in light.css stylesheet
245            "verbatimwithframe=false",
246            "VerbatimColor={HTML}{f0f2f4}",
247            "InnerLinkColor={HTML}{2980b9}",
248            "warningBgColor={HTML}{e9a499}",
249            "warningborder=0pt",
250            r"HeaderFamily=\rmfamily\bfseries",
251        )
252    ),
253}
254latex_logo = str(ZEPHYR_BASE / "doc" / "_static" / "images" / "logo-latex.pdf")
255latex_documents = [
256    ("index-tex", "zephyr.tex", "Zephyr Project Documentation", author, "manual"),
257]
258latex_engine = "xelatex"
259
260# -- Options for zephyr.doxyrunner plugin ---------------------------------
261
262doxyrunner_doxygen = os.environ.get("DOXYGEN_EXECUTABLE", "doxygen")
263doxyrunner_projects = {
264    "zephyr": {
265        "doxyfile": ZEPHYR_BASE / "doc" / "zephyr.doxyfile.in",
266        "outdir": ZEPHYR_BUILD / "doxygen",
267        "fmt": True,
268        "fmt_vars": {
269            "ZEPHYR_BASE": str(ZEPHYR_BASE),
270            "ZEPHYR_VERSION": version,
271        },
272        "outdir_var": "DOXY_OUT",
273    },
274}
275os.environ["DOXYGEN_SITEMAP_URL"] = f"{html_baseurl}doxygen/html"
276
277# -- Options for zephyr.doxybridge plugin ---------------------------------
278
279doxybridge_projects = {"zephyr": doxyrunner_projects["zephyr"]["outdir"]}
280
281# -- Options for html_redirect plugin -------------------------------------
282
283html_redirect_pages = redirects.REDIRECTS
284
285# -- Options for zephyr.link-roles ----------------------------------------
286
287link_roles_manifest_project = "zephyr"
288link_roles_manifest_project_broken_links_ignore_globs = [
289    "releases/release-notes-[123].*.rst",
290]
291link_roles_manifest_baseurl = "https://github.com/zephyrproject-rtos/zephyr"
292
293# -- Options for notfound.extension ---------------------------------------
294
295notfound_urls_prefix = f"/{version}/" if is_release else "/latest/"
296
297# -- Options for zephyr.gh_utils ------------------------------------------
298
299gh_link_version = f"v{version}" if is_release else "main"
300gh_link_base_url = "https://github.com/zephyrproject-rtos/zephyr"
301gh_link_prefixes = {
302    "samples/.*": "",
303    "boards/.*": "",
304    "snippets/.*": "",
305    ".*": "doc",
306}
307gh_link_exclude = [
308    "reference/kconfig.*",
309    "build/dts/api/bindings.*",
310    "build/dts/api/compatibles.*",
311]
312
313# -- Options for zephyr.kconfig -------------------------------------------
314
315kconfig_generate_db = True
316kconfig_ext_paths = [ZEPHYR_BASE]
317kconfig_gh_link_base_url = "https://github.com/zephyrproject-rtos/zephyr"
318kconfig_zephyr_version = f"v{version}" if is_release else "main"
319
320# -- Options for zephyr.external_content ----------------------------------
321
322external_content_contents = [
323    (ZEPHYR_BASE / "doc", "[!_]*"),
324    (ZEPHYR_BASE, "boards/**/*.rst"),
325    (ZEPHYR_BASE, "boards/**/doc"),
326    (ZEPHYR_BASE, "samples/**/*.html"),
327    (ZEPHYR_BASE, "samples/**/*.rst"),
328    (ZEPHYR_BASE, "samples/**/doc"),
329    (ZEPHYR_BASE, "snippets/**/*.rst"),
330    (ZEPHYR_BASE, "snippets/**/doc"),
331    (ZEPHYR_BASE, "tests/**/*.pts"),
332]
333external_content_keep = [
334    "reference/kconfig/*",
335    "develop/manifest/index.rst",
336    "build/dts/api/bindings.rst",
337    "build/dts/api/bindings/**/*",
338    "build/dts/api/compatibles/**/*",
339]
340
341# -- Options for zephyr.domain --------------------------------------------
342
343zephyr_breathe_insert_related_samples = True
344zephyr_generate_hw_features = not tags.has("hw_features_turbo")  # pylint: disable=undefined-variable  # noqa: F821
345zephyr_hw_features_vendor_filter = []
346
347# -- Options for sphinx.ext.graphviz --------------------------------------
348
349graphviz_dot = os.environ.get("DOT_EXECUTABLE", "dot")
350graphviz_output_format = "svg"
351graphviz_dot_args = [
352    "-Gbgcolor=transparent",
353    "-Nstyle=filled",
354    "-Nfillcolor=white",
355    "-Ncolor=gray60",
356    "-Nfontcolor=gray25",
357    "-Ecolor=gray60",
358    "-Gfontname=system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif",
359    "-Nfontname=system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif",
360    "-Efontname=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace",
361]
362
363# -- Options for sphinx_copybutton ----------------------------------------
364
365copybutton_prompt_text = r"\$ |uart:~\$ "
366copybutton_prompt_is_regexp = True
367
368# -- Options for sphinx-sitemap ----------------------------------------
369
370sitemap_url_scheme = "{link}"
371
372# -- Linkcheck options ----------------------------------------------------
373
374linkcheck_ignore = [
375    r"https://github.com/zephyrproject-rtos/zephyr/issues/.*"
376]
377
378extlinks = {
379    "github": ("https://github.com/zephyrproject-rtos/zephyr/issues/%s", "GitHub #%s"),
380}
381
382linkcheck_timeout = 30
383linkcheck_workers = 10
384linkcheck_anchors = False
385
386# -- Options for zephyr.api_overview --------------------------------------
387
388api_overview_doxygen_out_dir = str(doxyrunner_projects["zephyr"]["outdir"])
389api_overview_base_url = "https://github.com/zephyrproject-rtos/zephyr"
390
391def setup(app):
392    # theme customizations
393    app.add_css_file("css/custom.css")
394    app.add_js_file("js/custom.js")
395