Skip to content

Commit c6b05e4

Browse files
committed
Merge branch 'docs/soc_capability_tags' into 'master'
docs(ESPAT-2061): Enabled soc capability tags for target-specific doc builds See merge request application/esp-at!2051
2 parents a6e9bc3 + 8e67226 commit c6b05e4

6 files changed

Lines changed: 223 additions & 20 deletions

File tree

.gitlab/ci/build_docs.yml

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
.build_docs_script: &build_docs_script
2+
- cd docs
3+
- ./check_lang_folder_sync.sh
4+
- ./check_doc_chars.py
5+
- pip install -r requirements.txt
6+
- build-docs -t $DOCTGT -bs $DOC_BUILDERS -l $DOCLANG build
7+
18
.build_docs_template:
29
extends:
310
- .rules:build:build_doc
411
stage: build
5-
image: $CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.4:1-1
12+
image: $IDF_IMAGE
613
needs: []
714
tags:
815
- build_docs
@@ -13,25 +20,37 @@
1320
- docs/_build/*/*/latex/*
1421
- docs/_build/*/*/html/*
1522
expire_in: 6 mos
23+
before_script:
24+
# esp-at-doc-env images ship esp-idf at /opt/esp/idf. GitLab does not run
25+
# the image entrypoint, so export IDF_PATH + toolchain here.
26+
- . /opt/esp/idf/export.sh
1627
script:
17-
- cd docs
18-
- ./check_lang_folder_sync.sh
19-
- ./check_doc_chars.py
20-
- pip install -r requirements.txt
21-
- build-docs -t $DOCTGT -bs $DOC_BUILDERS -l $DOCLANG build
22-
parallel:
23-
matrix:
24-
- DOCLANG: ["en", "zh_CN"]
25-
DOCTGT: ["esp32", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32c61", "esp32s2"]
28+
- *build_docs_script
2629

2730
build_docs_html:
2831
extends:
2932
- .build_docs_template
3033
variables:
3134
DOC_BUILDERS: "html"
35+
parallel:
36+
matrix:
37+
- DOCLANG: ["en", "zh_CN"]
38+
DOCTGT: ["esp32", "esp32c2", "esp32c3", "esp32c6", "esp32s2"]
39+
IDF_IMAGE: $CI_REGISTRY/ci/images/esp-at-doc-env-v5.4:1
40+
- DOCLANG: ["en", "zh_CN"]
41+
DOCTGT: ["esp32c5", "esp32c61"]
42+
IDF_IMAGE: $CI_REGISTRY/ci/images/esp-at-doc-env-v5.5:1
3243

3344
build_docs_pdf:
3445
extends:
3546
- .build_docs_template
3647
variables:
3748
DOC_BUILDERS: "latex"
49+
parallel:
50+
matrix:
51+
- DOCLANG: ["en", "zh_CN"]
52+
DOCTGT: ["esp32", "esp32c2", "esp32c3", "esp32c6", "esp32s2"]
53+
IDF_IMAGE: $CI_REGISTRY/ci/images/esp-at-doc-env-v5.4:1
54+
- DOCLANG: ["en", "zh_CN"]
55+
DOCTGT: ["esp32c5", "esp32c61"]
56+
IDF_IMAGE: $CI_REGISTRY/ci/images/esp-at-doc-env-v5.5:1

docs/at_extensions/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import json
5+
import os
6+
import shutil
7+
import subprocess
8+
9+
from esp_docs.idf_extensions.build_system import project_path as dummy_project_path
10+
11+
from at_extensions.idf_env import get_idf_python, prepare_idf_env
12+
13+
14+
class AtIdfBuilder:
15+
def __init__(self) -> None:
16+
self.project_description = {}
17+
18+
def _run_idf_py(self, idf_py, args, cmake_build_dir):
19+
result = subprocess.run(idf_py + args, env=os.environ.copy(), text=True, capture_output=True)
20+
if result.returncode == 0:
21+
return
22+
23+
details = '\n'.join(part for part in [result.stdout, result.stderr] if part and part.strip()).strip()
24+
target = args[-1] if 'set-target' in args else 'unknown'
25+
raise RuntimeError(
26+
'Dummy ESP-IDF project setup failed for target {} (exit code {}).\n'
27+
'Install tools with: python $IDF_PATH/tools/idf_tools.py install\n'
28+
'{}'
29+
.format(target, result.returncode, details)
30+
)
31+
32+
def generate_idf_info(self, app, config):
33+
os.environ['IDF_DOC_BUILD'] = 'y'
34+
35+
if not app.config.idf_target:
36+
raise RuntimeError(
37+
'A valid target is needed to build ESP-AT docs. '
38+
'Please re-run build-docs with a target specified, e.g: build-docs -t esp32'
39+
)
40+
41+
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
42+
cmake_build_dir = os.path.join(build_dir, 'build_dummy_project')
43+
idf_path = prepare_idf_env()
44+
idf_py = [
45+
get_idf_python(),
46+
os.path.join(idf_path, 'tools', 'idf.py'),
47+
'-B',
48+
cmake_build_dir,
49+
'-C',
50+
dummy_project_path,
51+
'-D',
52+
'SDKCONFIG={}'.format(os.path.join(build_dir, 'dummy_project_sdkconfig')),
53+
]
54+
55+
shutil.rmtree(cmake_build_dir, ignore_errors=True)
56+
print('Starting dummy IDF project for ESP-AT docs (IDF_PATH={})...'.format(idf_path))
57+
self._run_idf_py(idf_py, ['--preview', 'set-target', app.config.idf_target], cmake_build_dir)
58+
self._run_idf_py(idf_py, ['reconfigure'], cmake_build_dir)
59+
60+
with open(os.path.join(cmake_build_dir, 'project_description.json')) as f:
61+
self.project_description = json.load(f)
62+
63+
if self.project_description['target'] != app.config.idf_target:
64+
raise RuntimeError(
65+
'Dummy IDF project target mismatch: expected {}, got {}.'
66+
.format(app.config.idf_target, self.project_description['target'])
67+
)
68+
69+
app.emit('project-build-info', self.project_description)
70+
return []
71+
72+
73+
at_idf_builder = AtIdfBuilder()
74+
75+
76+
def setup(app):
77+
try:
78+
build_dir = os.environ['BUILDDIR']
79+
except KeyError:
80+
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
81+
82+
for directory in (build_dir, os.path.join(build_dir, 'inc')):
83+
try:
84+
os.mkdir(directory)
85+
except OSError:
86+
pass
87+
88+
app.add_event('project-build-info')
89+
app.connect('config-inited', at_idf_builder.generate_idf_info)
90+
91+
return {'parallel_read_safe': True, 'parallel_write_safe': True, 'version': '0.1'}

docs/at_extensions/idf_env.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import os
5+
import subprocess
6+
import sys
7+
8+
_DOCS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
9+
_AT_ROOT = os.path.abspath(os.path.join(_DOCS_DIR, '..'))
10+
_PREPARED = False
11+
12+
13+
def _valid_idf_path(path):
14+
return path and os.path.isfile(os.path.join(path, 'tools', 'idf.py'))
15+
16+
17+
def resolve_idf_path():
18+
env_idf_path = os.environ.get('IDF_PATH')
19+
if _valid_idf_path(env_idf_path):
20+
return os.path.realpath(env_idf_path)
21+
22+
for candidate in (os.path.join(_AT_ROOT, 'esp-idf'), '/opt/esp/idf'):
23+
if _valid_idf_path(candidate):
24+
return os.path.realpath(candidate)
25+
26+
raise RuntimeError(
27+
'Cannot find ESP-IDF for documentation build. '
28+
'Set IDF_PATH, use an image that ships esp-idf at /opt/esp/idf, or clone esp-idf into esp-at/esp-idf.'
29+
)
30+
31+
32+
def export_idf_tools(idf_root):
33+
export_cmd = [
34+
sys.executable,
35+
os.path.join(idf_root, 'tools', 'idf_tools.py'),
36+
'--non-interactive',
37+
'export',
38+
'--format=key-value',
39+
]
40+
try:
41+
output = subprocess.check_output(export_cmd, stderr=subprocess.STDOUT, text=True)
42+
except subprocess.CalledProcessError as exc:
43+
raise RuntimeError(
44+
'Failed to export ESP-IDF build tools from {}.\n'
45+
'Install the required tools first:\n'
46+
' python {}/tools/idf_tools.py install\n'
47+
'Command output:\n{}'
48+
.format(idf_root, idf_root, exc.output)
49+
) from exc
50+
51+
for line in output.splitlines():
52+
if not line or '=' not in line:
53+
continue
54+
key, value = line.split('=', 1)
55+
if key == 'PATH':
56+
os.environ['PATH'] = value + os.pathsep + os.environ.get('PATH', '')
57+
else:
58+
os.environ[key] = value
59+
60+
61+
def get_idf_python():
62+
idf_python_env = os.environ.get('IDF_PYTHON_ENV_PATH')
63+
if idf_python_env:
64+
idf_python = os.path.join(idf_python_env, 'bin', 'python')
65+
if os.path.isfile(idf_python):
66+
return idf_python
67+
return sys.executable
68+
69+
70+
def prepare_idf_env():
71+
global _PREPARED
72+
if _PREPARED:
73+
return os.environ['IDF_PATH']
74+
75+
idf_root = resolve_idf_path()
76+
os.environ['IDF_PATH'] = idf_root
77+
export_idf_tools(idf_root)
78+
_PREPARED = True
79+
return idf_root

docs/conf_common.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66

77
from __future__ import print_function, unicode_literals
88

9+
import os
910
import os.path
11+
import subprocess
12+
import sys
13+
14+
docs_dir = os.path.dirname(os.path.abspath(__file__))
15+
sys.path.insert(0, docs_dir)
16+
17+
from at_extensions.idf_env import prepare_idf_env # noqa: E402
18+
19+
prepare_idf_env()
1020

1121
#ESP_DOCS_PATH = os.environ['ESP_DOCS_PATH']
1222

@@ -56,22 +66,24 @@
5666

5767
ESP32S2_DOCS = []
5868

59-
# format: {tag needed to include: documents to included}, tags are parsed from sdkconfig and peripheral_caps.h headers
69+
# format: {tag needed to include: documents to include}
70+
# Tags are chip names passed by build-docs -t <target> (e.g. esp32, esp32c3).
6071
conditional_include_dict = {
61-
'esp32':ESP32_DOCS,
62-
'esp32c2':ESP32C2_DOCS,
63-
'esp32c3':ESP32C3_DOCS,
64-
'esp32c5':ESP32C5_DOCS,
65-
'esp32c6':ESP32C6_DOCS,
66-
'esp32c61':ESP32C61_DOCS,
67-
'esp32s2':ESP32S2_DOCS
72+
'esp32': ESP32_DOCS,
73+
'esp32c2': ESP32C2_DOCS,
74+
'esp32c3': ESP32C3_DOCS,
75+
'esp32c5': ESP32C5_DOCS,
76+
'esp32c6': ESP32C6_DOCS,
77+
'esp32c61': ESP32C61_DOCS,
78+
'esp32s2': ESP32S2_DOCS,
6879
}
6980

7081
extensions += ['sphinx_copybutton',
7182
# Note: order is important here, events must
7283
# be registered by one extension before they can be
7384
# connected to another extension
74-
'esp_docs.esp_extensions.dummy_build_system',
85+
'at_extensions.idf_build_system',
86+
'esp_docs.idf_extensions.gen_defines',
7587
'esp_docs.esp_extensions.run_doxygen',
7688
]
7789

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
esp-docs==2.1.0
1+
esp-docs==2.5.1

0 commit comments

Comments
 (0)