-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdoxygen.py
166 lines (142 loc) · 5.72 KB
/
doxygen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import SCons
import collections
import os
import re
import subprocess
def warning(message, path, line_number = None):
"""Print a gcc-style warning"""
print(
'{}:{}: warning: {}'.format(
path,
str(line_number) if line_number else 0,
message))
class DoxygenConfig:
"""In-memory Doxygen configuration"""
class _Value:
"""Line number and value for a configuration key"""
def __init__(self, value, line_number = None):
self.value = value
self.line_number = line_number
def __init__(self, config_path):
self.original_path = str(config_path.srcnode())
with open(str(config_path), 'r') as config_file:
self._config = self._parse(config_file)
def _parse(self, config_file):
"""Parse Doxygen configuration file into an OrderedDict"""
kv = collections.OrderedDict()
line_number = 0
for line in config_file.readlines():
line_number += 1
# Strip comment
line = line[:line.find('#')]
# Look for key = value
match = re.search(r'(\w+)\s*=\s*(.*)', line)
if match:
key, value = match.group(1, 2)
kv[key] = self._Value(value.strip(), line_number)
return kv
def override_config_value(self, key, value):
"""Set a configuration value, warn if already set"""
if key in self._config:
if self._config[key].value != '':
warning(
'overriding configuration {}'.format(key),
self.original_path,
self._config[key].line_number)
self._config[key].value = value
else:
self._config[key] = self._Value(value)
def ensure_config_value(self, key, expected_value):
"""Ensure that a configuration value is correct
If the key already has the expected_value, do nothing.
Otherwise, print a warning and set the correct value.
"""
if key in self._config:
if self._config[key].value != expected_value:
warning(
'overriding configuration {} with {}'.format(
key, expected_value),
self.original_path,
self._config[key].line_number)
else:
warning(
'warning: adding missing configuration {} = {}'.format(
key, expected_value),
self.original_path)
self._config[key] = expected_value
def write_config(self, path):
"""Write Doxygen configuration dict to a file"""
with open(path, 'w') as f:
for k in self._config:
f.write('{} = {}\n'.format(k, self._config[k].value))
class DoxygenBinary:
@staticmethod
def is_runnable():
"""Return true if Doxygen runs, false otherwise"""
try:
subprocess.check_output('doxygen', stderr=subprocess.STDOUT)
return True
except subprocess.CalledProcessError:
return True
except OSError:
return False
@staticmethod
def invoke(config_path):
"""Run Doxygen on specificed configuration file"""
subprocess.call(['doxygen', config_path])
# Set up command line args used by every scons script
def common_arguments(env):
env.MBAddOption(
'--doxygen',
dest='doxygen',
action='store_true',
default=False,
help='Produces doxygen stuff.')
def generate(env):
env.Tool('options')
common_arguments(env)
expected_output = os.path.join('html', 'index.html')
def DoxygenBuilderAction(env, target, source):
if DoxygenBinary.is_runnable():
# Strip off the expected portion of the output path
target = target[0].get_abspath()
if target.endswith(expected_output):
target = target[:-len(expected_output)]
else:
raise Exception('Only HTML output is currently supported')
# Split source list into configuration file and everything else
config_path = source[0]
source = source[1:]
# Modify configuration
config = DoxygenConfig(config_path)
config.override_config_value('INPUT', ' '.join(str(s) for s in source))
config.override_config_value('OUTPUT_DIRECTORY', target)
config.ensure_config_value('GENERATE_HTML', 'YES')
config.ensure_config_value('HTML_OUTPUT', 'html')
# Write generated Doxygen configuration file
config_subst_path = str(config_path) + '.subst'
config.write_config(config_subst_path)
# Generate Doxygen output
DoxygenBinary.invoke(config_subst_path)
def DoxygenPrintAction(env, target, source):
print('Building Doxygen from {}'.format(str(target[0])))
env.Append(
BUILDERS = {
'DoxygenBuilder':
env.Builder(action = env.Action(
DoxygenBuilderAction,
DoxygenPrintAction))})
def BuildDoxygen(env, configuration, *sources):
"""Run Doxygen using configuration on specified sources"""
if env.MBGetOption('doxygen'):
all_source = (configuration,) + sources
env.DoxygenBuilder(
'doxygen/' + expected_output,
all_source)
# Ensure that documentation is built if only the
# "install" target is specified
# TODO: that ^ is not what this v does.
env.Append(MB_INSTALL_TARGETS = env.File('doxygen/html/index.html'))
env.AddMethod(BuildDoxygen, 'BuildDoxygen')
def exists(env):
return True