1""" A cmake extension for Sphinx 2 3tailored for the Dune project. 4This is used during `make doc` to build the 5build system documentation. 6""" 7 8from docutils import nodes 9from docutils.parsers.rst import Directive 10from itertools import chain 11 12class CMakeParamNode(nodes.Element): 13 pass 14 15class CMakeBriefNode(nodes.Element): 16 pass 17 18class CMakeFunction(Directive): 19 # We do require the name to be an argument 20 required_arguments = 1 21 optional_arguments = 0 22 final_argument_whitespace = False 23 has_content = True 24 25 def run(self): 26 env = self.state.document.settings.env 27 28 # Parse the content of the directive recursively 29 node = nodes.Element() 30 node.document = self.state.document 31 self.state.nested_parse(self.content, self.content_offset, node) 32 33 brief_nodes = [] 34 output_nodes = [] 35 positional_params = [] 36 required_params = {} 37 optional_params = {} 38 39 for child in node: 40 if isinstance(child, CMakeParamNode): 41 if child["positional"]: 42 positional_params.append(child) 43 elif child["required"]: 44 required_params[child["name"]] = child 45 else: 46 optional_params[child["name"]] = child 47 elif isinstance(child, CMakeBriefNode): 48 par = nodes.paragraph() 49 self.state.nested_parse(child['content'], self.content_offset, par) 50 brief_nodes.append(par) 51 else: 52 output_nodes.append(child) 53 54 def render_required(paramnode): 55 if paramnode["multi"]: 56 sl.append(" "*5 + paramnode['name'] + ' ' + paramnode['argname'] + '1 [' + paramnode['argname'] + '2 ...]\n') 57 if paramnode["single"]: 58 sl.append(" "*5 + paramnode['name'] + ' ' + paramnode['argname'] + '\n') 59 if paramnode["option"]: 60 sl.append(" "*5 + paramnode['name'] + '\n') 61 if paramnode["special"]: 62 sl.append(" "*5 + paramnode['argname'] + '\n') 63 64 def render_optional(paramnode): 65 if paramnode["multi"]: 66 sl.append(' '*4 + '[' + paramnode['name'] + ' ' + paramnode['argname'] + '1 [' + paramnode['argname'] + '2 ...]' + ']\n') 67 if paramnode["single"]: 68 sl.append(" "*4 + '['+ paramnode['name'] + ' ' + paramnode['argname'] + ']\n') 69 if paramnode["option"]: 70 sl.append(" "*4 + '['+ paramnode['name'] + ']\n') 71 if paramnode["special"]: 72 sl.append(" "*4 + '['+ paramnode['argname'] + ']\n') 73 74 # Build the content of the box 75 sl = [self.arguments[0] + '(\n'] 76 77 for paramnode in positional_params: 78 if paramnode["required"]: 79 render_required(paramnode) 80 else: 81 render_optional(paramnode) 82 83 for rp, paramnode in required_params.items(): 84 render_required(paramnode) 85 for op, paramnode in optional_params.items(): 86 render_optional(paramnode) 87 88 sl.append(")\n") 89 lb = nodes.literal_block(''.join(sl), ''.join(sl)) 90 brief_nodes.append(lb) 91 92 dl = nodes.definition_list() 93 for paramnode in chain(positional_params, required_params.values(), optional_params.values()): 94 dli = nodes.definition_list_item() 95 dl += dli 96 97 dlit = nodes.term(text=paramnode["name"]) 98 dli += dlit 99 100 dlic = nodes.definition() 101 dli += dlic 102 self.state.nested_parse(paramnode['content'], self.content_offset, dlic) 103 104 # add the parameter list to the output 105 brief_nodes.append(dl) 106 107 return brief_nodes + output_nodes 108 109class CMakeBrief(Directive): 110 required_arguments = 0 111 optional_arguments = 0 112 final_argument_whitespace = False 113 has_content = True 114 115 def run(self): 116 node = CMakeBriefNode() 117 node['content'] = self.content 118 return [node] 119 120class CMakeParam(Directive): 121 # We do require the name to be an argument 122 required_arguments = 1 123 optional_arguments = 0 124 final_argument_whitespace = False 125 option_spec = {'argname' : lambda s: s, 126 'multi': lambda s: True, 127 'option': lambda s: True, 128 'positional' : lambda s: True, 129 'required': lambda s: True, 130 'single': lambda s: True, 131 'special': lambda s: True 132 } 133 has_content = True 134 135 def run(self): 136 node = CMakeParamNode() 137 # set defaults: 138 node['name'] = self.arguments[0] 139 node['single'] = self.options.get('single', False) 140 node['multi'] = self.options.get('multi', False) 141 node['option'] = self.options.get('option', False) 142 node['special'] = self.options.get('special', False) 143 node['positional'] = self.options.get('positional', False) 144 node['required'] = self.options.get('required', False) 145 node['argname'] = self.options.get('argname', self.arguments[0].lower() if self.arguments[0].lower()[-1:] != 's' else self.arguments[0].lower()[:-1]) 146 node['content'] = self.content 147 if node['positional']: 148 node['argname'] = '' 149 return [node] 150 151 152class CMakeVariable(Directive): 153 # We do require the name to be an argument 154 required_arguments = 1 155 optional_arguments = 0 156 final_argument_whitespace = False 157 option_spec = {'argname' : lambda s: s, 158 'multi': lambda s: True, 159 'option': lambda s: True, 160 'positional' : lambda s: True, 161 'required': lambda s: True, 162 'single': lambda s: True 163 } 164 has_content = True 165 166 def run(self): 167 node = nodes.paragraph() 168 self.state.nested_parse(self.content, self.content_offset, node) 169 return [node] 170 171class CMakeModule(Directive): 172 required_arguments = 0 173 optional_arguments = 0 174 final_argument_whitespace = False 175 has_content = True 176 177 def run(self): 178 node = nodes.paragraph() 179 self.state.nested_parse(self.content, self.content_offset, node) 180 return [node] 181 182def setup(app): 183 app.add_node(CMakeBriefNode) 184 app.add_node(CMakeParamNode) 185 app.add_directive('cmake_module', CMakeModule) 186 app.add_directive('cmake_brief', CMakeBrief) 187 app.add_directive('cmake_function', CMakeFunction) 188 app.add_directive('cmake_param', CMakeParam) 189 app.add_directive('cmake_variable', CMakeVariable) 190 191 return {'version': '0.1'} 192