628
|
1 |
#============================================================================
|
|
2 |
#Name : sphinx_ext.py
|
|
3 |
#Part of : Helium
|
645
|
4 |
#
|
628
|
5 |
#Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
6 |
#All rights reserved.
|
|
7 |
#This component and the accompanying materials are made available
|
|
8 |
#under the terms of the License "Eclipse Public License v1.0"
|
|
9 |
#which accompanies this distribution, and is available
|
|
10 |
#at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
11 |
#
|
|
12 |
#Initial Contributors:
|
|
13 |
#Nokia Corporation - initial contribution.
|
|
14 |
#
|
|
15 |
#Contributors:
|
|
16 |
#
|
|
17 |
#Description:
|
|
18 |
#===============================================================================
|
645
|
19 |
""" Custom Sphinx operations to help with Helium doc linking. """
|
|
20 |
|
628
|
21 |
import os
|
|
22 |
import re
|
645
|
23 |
import atexit
|
628
|
24 |
|
|
25 |
from docutils import nodes, utils
|
|
26 |
from docutils.parsers.rst import directives
|
|
27 |
|
|
28 |
import amara
|
|
29 |
|
645
|
30 |
tree = None
|
628
|
31 |
treecache = None
|
645
|
32 |
database_path = os.path.abspath(os.path.join(os.getcwd() + '/build', 'public_database.xml'))
|
|
33 |
|
|
34 |
# Error count for custom sphinx operations
|
|
35 |
exit_with_failure = 0
|
628
|
36 |
|
645
|
37 |
def check_cached_database():
|
|
38 |
""" Check the Ant database XML data is cached as needed. """
|
|
39 |
global tree
|
|
40 |
global treecache
|
|
41 |
|
|
42 |
if tree == None or treecache == None:
|
|
43 |
f = open(database_path)
|
|
44 |
tree = amara.parse(f)
|
|
45 |
|
|
46 |
treecache = {}
|
|
47 |
for project in tree.antDatabase.project:
|
|
48 |
for x in project.xml_children:
|
|
49 |
if hasattr(x, 'name'):
|
|
50 |
treecache[str(x.name)] = [str(project.name),'project']
|
|
51 |
if hasattr(tree.antDatabase, "antlib"):
|
|
52 |
for antlib in tree.antDatabase.antlib:
|
|
53 |
for x in antlib.xml_children:
|
|
54 |
if hasattr(x, 'name'):
|
|
55 |
treecache[str(x.name)] = [str(antlib.name),'antlib']
|
|
56 |
|
|
57 |
def handle_hlm_role(role, _, text, lineno, inliner, options=None, content=None): # pylint: disable=W0613
|
628
|
58 |
""" Process a custom Helium ReStructuredText role to link to a target, property or macro. """
|
|
59 |
if options == None:
|
|
60 |
options = {}
|
|
61 |
if content == None:
|
|
62 |
content = []
|
645
|
63 |
|
|
64 |
# See if the role is used to embed a API element field
|
|
65 |
if '[' in text:
|
|
66 |
role_data = _embed_role_field(role, text, lineno, inliner)
|
|
67 |
else:
|
|
68 |
role_data = _build_link(text, lineno, inliner, options)
|
|
69 |
|
|
70 |
return role_data
|
|
71 |
|
|
72 |
def _embed_role_field(role, text, lineno, inliner):
|
|
73 |
""" Insert the contents of an element field.
|
|
74 |
|
|
75 |
These take the form of e.g. hlm-p:`build.drive[summary]`
|
|
76 |
"""
|
|
77 |
messages = []
|
|
78 |
node = nodes.Text('', '')
|
|
79 |
|
|
80 |
field_match = re.search("(.*?)\[(.*?)\]", text)
|
|
81 |
if field_match != None:
|
|
82 |
element_name = field_match.group(1)
|
|
83 |
field_name = field_match.group(2)
|
|
84 |
if field_name != None and len(field_name) > 0:
|
|
85 |
field_value = find_field_value(role, element_name, field_name)
|
|
86 |
if field_value != None and len(field_value) > 0:
|
|
87 |
node = nodes.Text(field_value, utils.unescape(field_value))
|
|
88 |
else:
|
|
89 |
messages.append(inliner.reporter.error(('Field value cannot be found for API field: "%s".' % text), line=lineno))
|
|
90 |
else:
|
|
91 |
messages.append(inliner.reporter.error(('Invalid field name for API value replacement: "%s".' % text), line=lineno))
|
|
92 |
return [node], messages
|
|
93 |
|
|
94 |
def find_field_value(role, element_name, field_name):
|
|
95 |
""" Gets the value of a field from an API element. """
|
|
96 |
check_cached_database()
|
|
97 |
|
|
98 |
field_value = None
|
|
99 |
element = tree.xml_xpath('//' + roles[role] + "[name='" + element_name + "']")
|
|
100 |
|
|
101 |
if element != None and len(element) == 1:
|
|
102 |
field_value_list = element[0].xml_xpath(field_name)
|
|
103 |
if field_value_list != None and len(field_value_list) == 1:
|
|
104 |
field_value = str(field_value_list[0])
|
|
105 |
return field_value
|
|
106 |
|
|
107 |
|
|
108 |
def _build_link(text, lineno, inliner, options):
|
|
109 |
""" Build an HTML link to the API doc location for API element. """
|
|
110 |
global exit_with_failure
|
628
|
111 |
full_path_match = re.search(r"<document source=\"(.*?)\"", str(inliner.document))
|
|
112 |
full_path = full_path_match.group(1)
|
|
113 |
path_segment = full_path[full_path.index('\\doc\\') + 5:]
|
|
114 |
dir_levels = path_segment.count('\\')
|
|
115 |
(parent_type, parent_name) = get_root_element_name(text)
|
|
116 |
messages = []
|
645
|
117 |
|
|
118 |
# See if link can be built
|
628
|
119 |
if parent_type != None and parent_name != None:
|
|
120 |
href_text = text.replace('.', '-').lower()
|
|
121 |
api_path_segment = 'api/helium/' + parent_type + '-' + parent_name + '.html#' + href_text
|
|
122 |
relative_path = ('../' * dir_levels) + api_path_segment
|
|
123 |
api_doc_path = os.path.abspath(os.path.join(os.getcwd() + '/build/doc', api_path_segment))
|
|
124 |
node = nodes.reference(text, utils.unescape(text), refuri=relative_path, **options)
|
|
125 |
node = nodes.literal(text, '', node, **options)
|
645
|
126 |
# Or just insert the basic property text
|
628
|
127 |
else:
|
|
128 |
messages.append(inliner.reporter.error(('Missing API doc for "%s".' % text), line=lineno))
|
|
129 |
node = nodes.literal(text, utils.unescape(text))
|
645
|
130 |
# Error occurred so record this in order to return the total number as a failure when exiting the program
|
|
131 |
exit_with_failure += 1
|
628
|
132 |
return [node], messages
|
|
133 |
|
|
134 |
def get_root_element_name(text):
|
645
|
135 |
check_cached_database()
|
|
136 |
|
628
|
137 |
if text in treecache:
|
645
|
138 |
return (treecache[text][1], treecache[text][0])
|
628
|
139 |
return (None, None)
|
|
140 |
|
|
141 |
roles = {'hlm-t': 'target',
|
|
142 |
'hlm-p': 'property',
|
|
143 |
'hlm-m': 'macro',}
|
|
144 |
|
|
145 |
|
|
146 |
def setup(app):
|
|
147 |
""" Register custom RST roles for linking Helium targets, properties and macros
|
|
148 |
to the API documentation. """
|
|
149 |
for role in roles.keys():
|
|
150 |
app.add_role(role, handle_hlm_role)
|
|
151 |
app.add_description_unit('property', 'ant-prop', 'pair: %s; property')
|
|
152 |
app.add_description_unit('target', 'ant-target', 'pair: %s; target')
|
|
153 |
|
|
154 |
|
645
|
155 |
def check_for_failure():
|
|
156 |
""" Check whether we need to exit the program with a failure due to one or more errors in a custom Sphinx operation. """
|
|
157 |
if exit_with_failure:
|
|
158 |
raise SystemExit("EXCEPTION: Found %d error(s) of type '(ERROR/3) Missing API doc for <property>'" % (exit_with_failure) )
|
|
159 |
|
|
160 |
# Register a cleanup routine to handle exit with failure
|
|
161 |
atexit.register(check_for_failure)
|
|
162 |
|