1# Copyright (c) 2019 Intel Corporation 2# 3# SPDX-License-Identifier: Apache-2.0 4 5# based on http://protips.readthedocs.io/link-roles.html 6 7from __future__ import print_function 8from __future__ import unicode_literals 9import re 10import subprocess 11from docutils import nodes 12 13try: 14 import west.manifest 15 16 try: 17 west_manifest = west.manifest.Manifest.from_file() 18 except west.util.WestNotFound: 19 west_manifest = None 20except ImportError: 21 west_manifest = None 22 23 24def get_github_rev(): 25 try: 26 output = subprocess.check_output( 27 "git describe --exact-match", shell=True, stderr=subprocess.DEVNULL 28 ) 29 except subprocess.CalledProcessError: 30 return "main" 31 32 return output.strip().decode("utf-8") 33 34 35def setup(app): 36 app.add_role("zephyr_file", modulelink("zephyr")) 37 app.add_role("zephyr_raw", modulelink("zephyr", format="raw")) 38 app.add_role("module_file", modulelink()) 39 40 app.add_config_value("link_roles_manifest_baseurl", None, "env") 41 app.add_config_value("link_roles_manifest_project", None, "env") 42 43 # The role just creates new nodes based on information in the 44 # arguments; its behavior doesn't depend on any other documents. 45 return { 46 "parallel_read_safe": True, 47 "parallel_write_safe": True, 48 } 49 50 51def modulelink(default_module=None, format="blob"): 52 def role(name, rawtext, text, lineno, inliner, options={}, content=[]): 53 # Set default values 54 module = default_module 55 rev = get_github_rev() 56 config = inliner.document.settings.env.app.config 57 baseurl = config.link_roles_manifest_baseurl 58 trace = f"at '{inliner.parent.source}', line {lineno}" 59 60 m = re.search(r"(.*)\s*<(.*)>", text) 61 if m: 62 link_text = m.group(1) 63 link = m.group(2) 64 else: 65 link_text = text 66 link = text 67 68 module_match = re.search(r"(.+?):\s*(.+)", link) 69 if module_match: 70 module = module_match.group(1).strip() 71 link = module_match.group(2).strip() 72 73 # Try to get a module repository's GitHub URL from the manifest. 74 # 75 # This allows e.g. building the docs in downstream Zephyr-based 76 # software with forks of the zephyr repository, and getting 77 # :zephyr_file: / :zephyr_raw: output that links to the fork, 78 # instead of mainline zephyr. 79 projects = [p.name for p in west_manifest.projects] if west_manifest else [] 80 if module in projects: 81 project = west_manifest.get_projects([module])[0] 82 baseurl = project.url 83 rev = project.revision 84 # No module provided 85 elif module is None: 86 raise ValueError( 87 f"Role 'module_file' must take a module as an argument\n\t{trace}" 88 ) 89 # Invalid module provided 90 elif module != config.link_roles_manifest_project: 91 raise ModuleNotFoundError( 92 f"Module {module} not found in the west manifest\n\t{trace}" 93 ) 94 # Baseurl for manifest project not set 95 elif baseurl is None: 96 raise ValueError( 97 f"Configuration value `link_roles_manifest_baseurl` not set\n\t{trace}" 98 ) 99 100 url = f"{baseurl}/{format}/{rev}/{link}" 101 node = nodes.reference(rawtext, link_text, refuri=url, **options) 102 return [node], [] 103 104 return role 105