1# Sphinx extension to integrate IDF build system information 2# into the Sphinx Build 3# 4# Runs early in the Sphinx process, runs CMake to generate the dummy IDF project 5# in this directory - including resolving paths, etc. 6# 7# Then emits the new 'idf-info' event which has information read from IDF 8# build system, that other extensions can use to generate relevant data. 9import json 10import os.path 11import shutil 12import subprocess 13import sys 14 15# this directory also contains the dummy IDF project 16project_path = os.path.abspath(os.path.dirname(__file__)) 17 18# Targets which needs --preview to build 19PREVIEW_TARGETS = [] 20 21 22def setup(app): 23 # Setup some common paths 24 25 try: 26 build_dir = os.environ['BUILDDIR'] # TODO see if we can remove this 27 except KeyError: 28 build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep)) 29 30 try: 31 os.mkdir(build_dir) 32 except OSError: 33 pass 34 35 try: 36 os.mkdir(os.path.join(build_dir, 'inc')) 37 except OSError: 38 pass 39 40 # Fill in a default IDF_PATH if it's missing (ie when Read The Docs is building the docs) 41 try: 42 idf_path = os.environ['IDF_PATH'] 43 except KeyError: 44 idf_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) 45 46 app.add_config_value('docs_root', os.path.join(idf_path, 'docs'), 'env') 47 app.add_config_value('idf_path', idf_path, 'env') 48 app.add_config_value('build_dir', build_dir, 'env') # not actually an IDF thing 49 app.add_event('idf-info') 50 51 # we want this to run early in the docs build but unclear exactly when 52 app.connect('config-inited', generate_idf_info) 53 54 return {'parallel_read_safe': True, 'parallel_write_safe': True, 'version': '0.1'} 55 56 57def generate_idf_info(app, config): 58 print('Running CMake on dummy project to get build info...') 59 build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep)) 60 cmake_build_dir = os.path.join(build_dir, 'build_dummy_project') 61 idf_py_path = os.path.join(app.config.idf_path, 'tools', 'idf.py') 62 print('Running idf.py...') 63 idf_py = [sys.executable, 64 idf_py_path, 65 '-B', 66 cmake_build_dir, 67 '-C', 68 project_path, 69 '-D', 70 'SDKCONFIG={}'.format(os.path.join(build_dir, 'dummy_project_sdkconfig')) 71 ] 72 73 # force a clean idf.py build w/ new sdkconfig each time 74 # (not much slower than 'reconfigure', avoids any potential config & build versioning problems 75 shutil.rmtree(cmake_build_dir, ignore_errors=True) 76 print('Starting new dummy IDF project... ') 77 78 if (app.config.idf_target in PREVIEW_TARGETS): 79 subprocess.check_call(idf_py + ['--preview', 'set-target', app.config.idf_target]) 80 else: 81 subprocess.check_call(idf_py + ['set-target', app.config.idf_target]) 82 83 print('Running CMake on dummy project...') 84 subprocess.check_call(idf_py + ['reconfigure']) 85 86 with open(os.path.join(cmake_build_dir, 'project_description.json')) as f: 87 project_description = json.load(f) 88 if project_description['target'] != app.config.idf_target: 89 # this shouldn't really happen unless someone has been moving around directories inside _build, as 90 # the cmake_build_dir path should be target-specific 91 raise RuntimeError(('Error configuring the dummy IDF project for {}. ' + 92 'Target in project description is {}. ' + 93 'Is build directory contents corrupt?') 94 .format(app.config.idf_target, project_description['target'])) 95 app.emit('idf-info', project_description) 96 97 return [] 98