diff --git a/docs/en/api-guides/build-system.rst b/docs/en/api-guides/build-system.rst index 9746213b0d..f2421acbcc 100644 --- a/docs/en/api-guides/build-system.rst +++ b/docs/en/api-guides/build-system.rst @@ -102,6 +102,7 @@ Advanced Commands - ``idf.py size`` prints some size information about the app. ``size-components`` and ``size-files`` are similar commands which print more detailed per-component or per-source-file information, respectively. If you define variable ``-DOUTPUT_JSON=1`` when running CMake (or ``idf.py``), the output will be formatted as JSON not as human readable text. See ``idf.py-size`` for more information. - ``idf.py reconfigure`` re-runs CMake_ even if it doesn't seem to need re-running. This isn't necessary during normal usage, but can be useful after adding/removing files from the source tree, or when modifying CMake cache variables. For example, ``idf.py -DNAME='VALUE' reconfigure`` can be used to set variable ``NAME`` in CMake cache to value ``VALUE``. - ``idf.py python-clean`` deletes generated Python byte code from the IDF directory which may cause issues when switching between IDF and Python versions. It is advised to run this target after switching versions of Python. +- ``idf.py docs`` will open direct link to documentation for project's chip target and version in browser. To see all options use ``idf.py docs --help`` The order of multiple ``idf.py`` commands on the same invocation is not important, they will automatically be executed in the correct order for everything to take effect (ie building before flashing, erasing before flashing, etc.). diff --git a/tools/ci/test_build_system_cmake.sh b/tools/ci/test_build_system_cmake.sh index b5787a8f73..ce349765f8 100755 --- a/tools/ci/test_build_system_cmake.sh +++ b/tools/ci/test_build_system_cmake.sh @@ -863,6 +863,17 @@ endmenu\n" >> ${IDF_PATH}/Kconfig expected_failure $EXPECTED_EXIT_VALUE idf.py create-project --path "$IDF_PATH/example_proj" temp_test_project || failure "Command exit value is wrong." rm -rf "$IDF_PATH/example_proj" + print_status "Check docs command" + clean_build_dir + idf.py build + idf.py set-target esp32s2 + idf.py docs || failure "'idf.py docs' failed" + idf.py docs --no-browser | grep "https://docs.espressif.com/projects/esp-idf/en" || failure "'idf.py docs --no-browser' failed" + idf.py docs --no-browser --language en | grep "https://docs.espressif.com/projects/esp-idf/en" || failure "'idf.py docs --no-browser --language en' failed" + idf.py docs --no-browser --language en --version v4.2.1 | grep "https://docs.espressif.com/projects/esp-idf/en/v4.2.1" || failure "'idf.py docs --no-browser --language en --version v4.2.1' failed" + idf.py docs --no-browser --language en --version v4.2.1 --target esp32 | grep "https://docs.espressif.com/projects/esp-idf/en/v4.2.1/esp32" || failure "'idf.py docs --no-browser --language en --version v4.2.1 --target esp32' failed" + idf.py docs --no-browser --language en --version v4.2.1 --target esp32 --starting-page get-started | grep "https://docs.espressif.com/projects/esp-idf/en/v4.2.1/esp32/get-started" || failure "'idf.py docs --no-browser --language en --version v4.2.1 --target esp32 --starting-page get-started' failed" + print_status "All tests completed" if [ -n "${FAILURES}" ]; then echo "Some failures were detected:" diff --git a/tools/idf_py_actions/constants.py b/tools/idf_py_actions/constants.py index a65b2b92b7..53c652c7fd 100644 --- a/tools/idf_py_actions/constants.py +++ b/tools/idf_py_actions/constants.py @@ -36,6 +36,7 @@ GENERATORS = collections.OrderedDict([ }) ]) -SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3'] +URL_TO_DOC = 'https://docs.espressif.com/projects/esp-idf' +SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3'] PREVIEW_TARGETS = ['linux', 'esp32h2'] diff --git a/tools/idf_py_actions/core_ext.py b/tools/idf_py_actions/core_ext.py index 4d294ca35d..1df2b817e4 100644 --- a/tools/idf_py_actions/core_ext.py +++ b/tools/idf_py_actions/core_ext.py @@ -1,15 +1,20 @@ import fnmatch +import locale import os +import re import shutil import subprocess import sys +from urllib.error import URLError +from urllib.request import Request, urlopen +from webbrowser import open_new_tab import click -from idf_py_actions.constants import GENERATORS, PREVIEW_TARGETS, SUPPORTED_TARGETS +from idf_py_actions.constants import GENERATORS, PREVIEW_TARGETS, SUPPORTED_TARGETS, URL_TO_DOC from idf_py_actions.errors import FatalError from idf_py_actions.global_options import global_options -from idf_py_actions.tools import (TargetChoice, ensure_build_directory, idf_version, merge_action_lists, realpath, - run_target) +from idf_py_actions.tools import (TargetChoice, ensure_build_directory, get_target, idf_version, merge_action_lists, + realpath, run_target) def action_extensions(base_actions, project_path): @@ -192,6 +197,44 @@ def action_extensions(base_actions, project_path): sys.exit(0) + def show_docs(action, ctx, args, no_browser, language, starting_page, version, target): + if language == 'cn': + language = 'zh_CN' + if not version: + # '0.0-dev' here because if 'dev' in version it will transform in to 'latest' + version = re.search(r'v\d+\.\d+\.?\d*(-dev|-beta\d|-rc)?', idf_version() or '0.0-dev').group() + if 'dev' in version: + version = 'latest' + elif version[0] != 'v': + version = 'v' + version + target = target or get_target(args.project_dir) or 'esp32' + link = '/'.join([URL_TO_DOC, language, version, target, starting_page or '']) + redirect_link = False + try: + req = Request(link) + webpage = urlopen(req) + redirect_link = webpage.geturl().endswith('404.html') + except URLError: + print("We can't check the link's functionality because you don't have an internet connection") + if redirect_link: + print('Target', target, 'doesn\'t exist for version', version) + link = '/'.join([URL_TO_DOC, language, version, starting_page or '']) + if not no_browser: + print('Opening documentation in the default browser:') + print(link) + open_new_tab(link) + else: + print('Please open the documentation link in the browser:') + print(link) + sys.exit(0) + + def get_default_language(): + try: + language = 'zh_CN' if locale.getdefaultlocale()[0] == 'zh_CN' else 'en' + except ValueError: + language = 'en' + return language + root_options = { 'global_options': [ { @@ -386,6 +429,36 @@ def action_extensions(base_actions, project_path): 'callback': fallback_target, 'help': 'Handle for targets not known for idf.py.', 'hidden': True, + }, + 'docs': { + 'callback': show_docs, + 'help': 'Open web browser with documentation for ESP-IDF', + 'options': [ + { + 'names': ['--no-browser', '-nb'], + 'is_flag': True, + 'help': 'Don\'t open browser.' + }, + { + 'names': ['--language', '-l'], + 'default': get_default_language(), + 'type': click.Choice(['en', 'zh_CN', 'cn']), + 'help': 'Documentation language. Your system language by default (en or cn)' + }, + { + 'names': ['--starting-page', '-sp'], + 'help': 'Documentation page (get-started, api-reference etc).' + }, + { + 'names': ['--version', '-v'], + 'help': 'Version of ESP-IDF.' + }, + { + 'names': ['--target', '-t'], + 'type': TargetChoice(SUPPORTED_TARGETS + PREVIEW_TARGETS + ['']), + 'help': 'Chip target.' + } + ] } } } diff --git a/tools/idf_py_actions/tools.py b/tools/idf_py_actions/tools.py index 78f74975d7..20e265e154 100644 --- a/tools/idf_py_actions/tools.py +++ b/tools/idf_py_actions/tools.py @@ -47,6 +47,11 @@ def _idf_version_from_cmake(): return None +def get_target(path, sdkconfig_filename='sdkconfig'): + path = os.path.join(path, sdkconfig_filename) + return get_sdkconfig_value(path, 'CONFIG_IDF_TARGET') + + def idf_version(): """Print version of ESP-IDF""" @@ -277,7 +282,7 @@ def is_target_supported(project_path, supported_targets): """ Returns True if the active target is supported, or False otherwise. """ - return get_sdkconfig_value(os.path.join(project_path, 'sdkconfig'), 'CONFIG_IDF_TARGET') in supported_targets + return get_target(project_path) in supported_targets def _guess_or_check_idf_target(args, prog_name, cache): @@ -290,12 +295,9 @@ def _guess_or_check_idf_target(args, prog_name, cache): """ # Default locations of sdkconfig files. # FIXME: they may be overridden in the project or by a CMake variable (IDF-1369). - sdkconfig_path = os.path.join(args.project_dir, 'sdkconfig') - sdkconfig_defaults_path = os.path.join(args.project_dir, 'sdkconfig.defaults') - # These are used to guess the target from sdkconfig, or set the default target by sdkconfig.defaults. - idf_target_from_sdkconfig = get_sdkconfig_value(sdkconfig_path, 'CONFIG_IDF_TARGET') - idf_target_from_sdkconfig_defaults = get_sdkconfig_value(sdkconfig_defaults_path, 'CONFIG_IDF_TARGET') + idf_target_from_sdkconfig = get_target(args.project_dir) + idf_target_from_sdkconfig_defaults = get_target(args.project_dir, 'sdkconfig.defaults') idf_target_from_env = os.environ.get('IDF_TARGET') idf_target_from_cache = cache.get('IDF_TARGET')