##// END OF EJS Templates
doc: add generic frontend to rst2man and rst2html...
Martin Geisler -
r10971:cbe400a8 default
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 #!/usr/bin/env python
2 #
3 # runrst - register custom roles and run correct writer
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com> and others
6 #
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
9
10 """usage: %s WRITER args...
11
12 where WRITER is the name of a Docutils writer such as 'html' or 'manpage'
13 """
14
15 import sys
16 from docutils.core import publish_cmdline
17
18 if __name__ == "__main__":
19 if len(sys.argv) < 2:
20 sys.stderr.write(__doc__ % sys.argv[0])
21 sys.exit(1)
22
23 writer = sys.argv[1]
24 del sys.argv[1]
25
26 publish_cmdline(writer_name=writer)
@@ -1,50 +1,49 b''
1 1 SOURCES=$(wildcard *.[0-9].txt)
2 2 MAN=$(SOURCES:%.txt=%)
3 3 HTML=$(SOURCES:%.txt=%.html)
4 4 GENDOC=gendoc.py ../mercurial/commands.py ../mercurial/help.py ../mercurial/help/*.txt
5 5 PREFIX=/usr/local
6 6 MANDIR=$(PREFIX)/share/man
7 7 INSTALL=install -c -m 644
8 8 PYTHON=python
9 RST2HTML=$(shell which rst2html 2> /dev/null || echo rst2html.py)
10 9
11 10 export LANGUAGE=C
12 11 export LC_ALL=C
13 12
14 13 all: man html
15 14
16 15 man: $(MAN)
17 16
18 17 html: $(HTML)
19 18
20 19 hg.1.txt: hg.1.gendoc.txt
21 20 touch hg.1.txt
22 21
23 22 hg.1.gendoc.txt: $(GENDOC)
24 23 ${PYTHON} gendoc.py > $@.tmp
25 24 mv $@.tmp $@
26 25
27 26 %: %.txt common.txt
28 $(PYTHON) rst2man.py --halt warning \
27 $(PYTHON) runrst manpage --halt warning \
29 28 --strip-elements-with-class htmlonly $*.txt $*
30 29
31 30 %.html: %.txt common.txt
32 $(RST2HTML) --halt warning \
31 $(PYTHON) runrst html --halt warning \
33 32 --link-stylesheet --stylesheet-path style.css $*.txt $*.html
34 33
35 34 MANIFEST: man html
36 35 # tracked files are already in the main MANIFEST
37 36 $(RM) $@
38 37 for i in $(MAN) $(HTML) hg.1.gendoc.txt; do \
39 38 echo "doc/$$i" >> $@ ; \
40 39 done
41 40
42 41 install: man
43 42 for i in $(MAN) ; do \
44 43 subdir=`echo $$i | sed -n 's/^.*\.\([0-9]\)$$/man\1/p'` ; \
45 44 mkdir -p $(DESTDIR)$(MANDIR)/$$subdir ; \
46 45 $(INSTALL) $$i $(DESTDIR)$(MANDIR)/$$subdir ; \
47 46 done
48 47
49 48 clean:
50 49 $(RM) $(MAN) $(MAN:%=%.html) *.[0-9].gendoc.txt MANIFEST
@@ -1,1109 +1,1102 b''
1 1 #!/usr/bin/env python
2 2 # -*- coding: utf-8 -*-
3 3 # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
4 4 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
5 5 # Copyright: This module is put into the public domain.
6 6
7 7 """
8 8 Simple man page writer for reStructuredText.
9 9
10 10 Man pages (short for "manual pages") contain system documentation on unix-like
11 11 systems. The pages are grouped in numbered sections:
12 12
13 13 1 executable programs and shell commands
14 14 2 system calls
15 15 3 library functions
16 16 4 special files
17 17 5 file formats
18 18 6 games
19 19 7 miscellaneous
20 20 8 system administration
21 21
22 22 Man pages are written *troff*, a text file formatting system.
23 23
24 24 See http://www.tldp.org/HOWTO/Man-Page for a start.
25 25
26 26 Man pages have no subsection only parts.
27 27 Standard parts
28 28
29 29 NAME ,
30 30 SYNOPSIS ,
31 31 DESCRIPTION ,
32 32 OPTIONS ,
33 33 FILES ,
34 34 SEE ALSO ,
35 35 BUGS ,
36 36
37 37 and
38 38
39 39 AUTHOR .
40 40
41 41 A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
42 42 by the command whatis or apropos.
43 43
44 44 """
45 45
46 46 __docformat__ = 'reStructuredText'
47 47
48 48 import re
49 49
50 50 from docutils import nodes, writers, languages
51 51 import roman
52 52
53 53 FIELD_LIST_INDENT = 7
54 54 DEFINITION_LIST_INDENT = 7
55 55 OPTION_LIST_INDENT = 7
56 56 BLOCKQOUTE_INDENT = 3.5
57 57
58 58 # Define two macros so man/roff can calculate the
59 59 # indent/unindent margins by itself
60 60 MACRO_DEF = (r""".
61 61 .nr rst2man-indent-level 0
62 62 .
63 63 .de1 rstReportMargin
64 64 \\$1 \\n[an-margin]
65 65 level \\n[rst2man-indent-level]
66 66 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
67 67 -
68 68 \\n[rst2man-indent0]
69 69 \\n[rst2man-indent1]
70 70 \\n[rst2man-indent2]
71 71 ..
72 72 .de1 INDENT
73 73 .\" .rstReportMargin pre:
74 74 . RS \\$1
75 75 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
76 76 . nr rst2man-indent-level +1
77 77 .\" .rstReportMargin post:
78 78 ..
79 79 .de UNINDENT
80 80 . RE
81 81 .\" indent \\n[an-margin]
82 82 .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
83 83 .nr rst2man-indent-level -1
84 84 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
85 85 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
86 86 ..
87 87 """)
88 88
89 89 class Writer(writers.Writer):
90 90
91 91 supported = ('manpage')
92 92 """Formats this writer supports."""
93 93
94 94 output = None
95 95 """Final translated form of `document`."""
96 96
97 97 def __init__(self):
98 98 writers.Writer.__init__(self)
99 99 self.translator_class = Translator
100 100
101 101 def translate(self):
102 102 visitor = self.translator_class(self.document)
103 103 self.document.walkabout(visitor)
104 104 self.output = visitor.astext()
105 105
106 106
107 107 class Table:
108 108 def __init__(self):
109 109 self._rows = []
110 110 self._options = ['center']
111 111 self._tab_char = '\t'
112 112 self._coldefs = []
113 113 def new_row(self):
114 114 self._rows.append([])
115 115 def append_separator(self, separator):
116 116 """Append the separator for table head."""
117 117 self._rows.append([separator])
118 118 def append_cell(self, cell_lines):
119 119 """cell_lines is an array of lines"""
120 120 start = 0
121 121 if len(cell_lines) > 0 and cell_lines[0] == '.sp\n':
122 122 start = 1
123 123 self._rows[-1].append(cell_lines[start:])
124 124 if len(self._coldefs) < len(self._rows[-1]):
125 125 self._coldefs.append('l')
126 126 def _minimize_cell(self, cell_lines):
127 127 """Remove leading and trailing blank and ``.sp`` lines"""
128 128 while (cell_lines and cell_lines[0] in ('\n', '.sp\n')):
129 129 del cell_lines[0]
130 130 while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')):
131 131 del cell_lines[-1]
132 132 def as_list(self):
133 133 text = ['.TS\n']
134 134 text.append(' '.join(self._options) + ';\n')
135 135 text.append('|%s|.\n' % ('|'.join(self._coldefs)))
136 136 for row in self._rows:
137 137 # row = array of cells. cell = array of lines.
138 138 text.append('_\n') # line above
139 139 text.append('T{\n')
140 140 for i in range(len(row)):
141 141 cell = row[i]
142 142 self._minimize_cell(cell)
143 143 text.extend(cell)
144 144 if not text[-1].endswith('\n'):
145 145 text[-1] += '\n'
146 146 if i < len(row)-1:
147 147 text.append('T}'+self._tab_char+'T{\n')
148 148 else:
149 149 text.append('T}\n')
150 150 text.append('_\n')
151 151 text.append('.TE\n')
152 152 return text
153 153
154 154 class Translator(nodes.NodeVisitor):
155 155 """"""
156 156
157 157 words_and_spaces = re.compile(r'\S+| +|\n')
158 158 document_start = """Man page generated from reStructeredText."""
159 159
160 160 def __init__(self, document):
161 161 nodes.NodeVisitor.__init__(self, document)
162 162 self.settings = settings = document.settings
163 163 lcode = settings.language_code
164 164 self.language = languages.get_language(lcode)
165 165 self.head = []
166 166 self.body = []
167 167 self.foot = []
168 168 self.section_level = 0
169 169 self.context = []
170 170 self.topic_class = ''
171 171 self.colspecs = []
172 172 self.compact_p = 1
173 173 self.compact_simple = None
174 174 # the list style "*" bullet or "#" numbered
175 175 self._list_char = []
176 176 # writing the header .TH and .SH NAME is postboned after
177 177 # docinfo.
178 178 self._docinfo = {
179 179 "title" : "", "title_upper": "",
180 180 "subtitle" : "",
181 181 "manual_section" : "", "manual_group" : "",
182 182 "author" : [],
183 183 "date" : "",
184 184 "copyright" : "",
185 185 "version" : "",
186 186 }
187 187 self._docinfo_keys = [] # a list to keep the sequence as in source.
188 188 self._docinfo_names = {} # to get name from text not normalized.
189 189 self._in_docinfo = None
190 190 self._active_table = None
191 191 self._in_literal = False
192 192 self.header_written = 0
193 193 self._line_block = 0
194 194 self.authors = []
195 195 self.section_level = 0
196 196 self._indent = [0]
197 197 # central definition of simple processing rules
198 198 # what to output on : visit, depart
199 199 # Do not use paragraph requests ``.PP`` because these set indentation.
200 200 # use ``.sp``. Remove superfluous ``.sp`` in ``astext``.
201 201 #
202 202 # Fonts are put on a stack, the top one is used.
203 203 # ``.ft P`` or ``\\fP`` pop from stack.
204 204 # ``B`` bold, ``I`` italic, ``R`` roman should be available.
205 205 # Hopefully ``C`` courier too.
206 206 self.defs = {
207 207 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
208 208 'definition_list_item' : ('.TP', ''),
209 209 'field_name' : ('.TP\n.B ', '\n'),
210 210 'literal' : ('\\fB', '\\fP'),
211 211 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
212 212
213 213 'option_list_item' : ('.TP\n', ''),
214 214
215 215 'reference' : (r'\%', r'\:'),
216 216 'emphasis': ('\\fI', '\\fP'),
217 217 'strong' : ('\\fB', '\\fP'),
218 218 'term' : ('\n.B ', '\n'),
219 219 'title_reference' : ('\\fI', '\\fP'),
220 220
221 221 'topic-title' : ('.SS ',),
222 222 'sidebar-title' : ('.SS ',),
223 223
224 224 'problematic' : ('\n.nf\n', '\n.fi\n'),
225 225 }
226 226 # NOTE don't specify the newline before a dot-command, but ensure
227 227 # it is there.
228 228
229 229 def comment_begin(self, text):
230 230 """Return commented version of the passed text WITHOUT end of
231 231 line/comment."""
232 232 prefix = '.\\" '
233 233 out_text = ''.join(
234 234 [(prefix + in_line + '\n')
235 235 for in_line in text.split('\n')])
236 236 return out_text
237 237
238 238 def comment(self, text):
239 239 """Return commented version of the passed text."""
240 240 return self.comment_begin(text)+'.\n'
241 241
242 242 def ensure_eol(self):
243 243 """Ensure the last line in body is terminated by new line."""
244 244 if self.body[-1][-1] != '\n':
245 245 self.body.append('\n')
246 246
247 247 def astext(self):
248 248 """Return the final formatted document as a string."""
249 249 if not self.header_written:
250 250 # ensure we get a ".TH" as viewers require it.
251 251 self.head.append(self.header())
252 252 # filter body
253 253 for i in xrange(len(self.body)-1, 0, -1):
254 254 # remove superfluous vertical gaps.
255 255 if self.body[i] == '.sp\n':
256 256 if self.body[i - 1][:4] in ('.BI ','.IP '):
257 257 self.body[i] = '.\n'
258 258 elif (self.body[i - 1][:3] == '.B ' and
259 259 self.body[i - 2][:4] == '.TP\n'):
260 260 self.body[i] = '.\n'
261 261 elif (self.body[i - 1] == '\n' and
262 262 self.body[i - 2][0] != '.' and
263 263 (self.body[i - 3][:7] == '.TP\n.B '
264 264 or self.body[i - 3][:4] == '\n.B ')
265 265 ):
266 266 self.body[i] = '.\n'
267 267 return ''.join(self.head + self.body + self.foot)
268 268
269 269 def deunicode(self, text):
270 270 text = text.replace(u'\xa0', '\\ ')
271 271 text = text.replace(u'\u2020', '\\(dg')
272 272 return text
273 273
274 274 def visit_Text(self, node):
275 275 text = node.astext()
276 276 text = text.replace('\\','\\e')
277 277 replace_pairs = [
278 278 (u'-', ur'\-'),
279 279 (u'\'', ur'\(aq'),
280 280 (u'´', ur'\''),
281 281 (u'`', ur'\(ga'),
282 282 ]
283 283 for (in_char, out_markup) in replace_pairs:
284 284 text = text.replace(in_char, out_markup)
285 285 # unicode
286 286 text = self.deunicode(text)
287 287 if self._in_literal:
288 288 # prevent interpretation of "." at line start
289 289 if text[0] == '.':
290 290 text = '\\&' + text
291 291 text = text.replace('\n.', '\n\\&.')
292 292 self.body.append(text)
293 293
294 294 def depart_Text(self, node):
295 295 pass
296 296
297 297 def list_start(self, node):
298 298 class enum_char:
299 299 enum_style = {
300 300 'bullet' : '\\(bu',
301 301 'emdash' : '\\(em',
302 302 }
303 303
304 304 def __init__(self, style):
305 305 self._style = style
306 306 if node.has_key('start'):
307 307 self._cnt = node['start'] - 1
308 308 else:
309 309 self._cnt = 0
310 310 self._indent = 2
311 311 if style == 'arabic':
312 312 # indentation depends on number of childrens
313 313 # and start value.
314 314 self._indent = len(str(len(node.children)))
315 315 self._indent += len(str(self._cnt)) + 1
316 316 elif style == 'loweralpha':
317 317 self._cnt += ord('a') - 1
318 318 self._indent = 3
319 319 elif style == 'upperalpha':
320 320 self._cnt += ord('A') - 1
321 321 self._indent = 3
322 322 elif style.endswith('roman'):
323 323 self._indent = 5
324 324
325 325 def next(self):
326 326 if self._style == 'bullet':
327 327 return self.enum_style[self._style]
328 328 elif self._style == 'emdash':
329 329 return self.enum_style[self._style]
330 330 self._cnt += 1
331 331 # TODO add prefix postfix
332 332 if self._style == 'arabic':
333 333 return "%d." % self._cnt
334 334 elif self._style in ('loweralpha', 'upperalpha'):
335 335 return "%c." % self._cnt
336 336 elif self._style.endswith('roman'):
337 337 res = roman.toRoman(self._cnt) + '.'
338 338 if self._style.startswith('upper'):
339 339 return res.upper()
340 340 return res.lower()
341 341 else:
342 342 return "%d." % self._cnt
343 343 def get_width(self):
344 344 return self._indent
345 345 def __repr__(self):
346 346 return 'enum_style-%s' % list(self._style)
347 347
348 348 if node.has_key('enumtype'):
349 349 self._list_char.append(enum_char(node['enumtype']))
350 350 else:
351 351 self._list_char.append(enum_char('bullet'))
352 352 if len(self._list_char) > 1:
353 353 # indent nested lists
354 354 self.indent(self._list_char[-2].get_width())
355 355 else:
356 356 self.indent(self._list_char[-1].get_width())
357 357
358 358 def list_end(self):
359 359 self.dedent()
360 360 self._list_char.pop()
361 361
362 362 def header(self):
363 363 tmpl = (".TH %(title_upper)s %(manual_section)s"
364 364 " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
365 365 ".SH NAME\n"
366 366 "%(title)s \- %(subtitle)s\n")
367 367 return tmpl % self._docinfo
368 368
369 369 def append_header(self):
370 370 """append header with .TH and .SH NAME"""
371 371 # NOTE before everything
372 372 # .TH title_upper section date source manual
373 373 if self.header_written:
374 374 return
375 375 self.body.append(self.header())
376 376 self.body.append(MACRO_DEF)
377 377 self.header_written = 1
378 378
379 379 def visit_address(self, node):
380 380 self.visit_docinfo_item(node, 'address')
381 381
382 382 def depart_address(self, node):
383 383 pass
384 384
385 385 def visit_admonition(self, node, name=None):
386 386 if name:
387 387 self.body.append('.IP %s\n' %
388 388 self.language.labels.get(name, name))
389 389
390 390 def depart_admonition(self, node):
391 391 self.body.append('.RE\n')
392 392
393 393 def visit_attention(self, node):
394 394 self.visit_admonition(node, 'attention')
395 395
396 396 depart_attention = depart_admonition
397 397
398 398 def visit_docinfo_item(self, node, name):
399 399 if name == 'author':
400 400 self._docinfo[name].append(node.astext())
401 401 else:
402 402 self._docinfo[name] = node.astext()
403 403 self._docinfo_keys.append(name)
404 404 raise nodes.SkipNode
405 405
406 406 def depart_docinfo_item(self, node):
407 407 pass
408 408
409 409 def visit_author(self, node):
410 410 self.visit_docinfo_item(node, 'author')
411 411
412 412 depart_author = depart_docinfo_item
413 413
414 414 def visit_authors(self, node):
415 415 # _author is called anyway.
416 416 pass
417 417
418 418 def depart_authors(self, node):
419 419 pass
420 420
421 421 def visit_block_quote(self, node):
422 422 # BUG/HACK: indent alway uses the _last_ indention,
423 423 # thus we need two of them.
424 424 self.indent(BLOCKQOUTE_INDENT)
425 425 self.indent(0)
426 426
427 427 def depart_block_quote(self, node):
428 428 self.dedent()
429 429 self.dedent()
430 430
431 431 def visit_bullet_list(self, node):
432 432 self.list_start(node)
433 433
434 434 def depart_bullet_list(self, node):
435 435 self.list_end()
436 436
437 437 def visit_caption(self, node):
438 438 pass
439 439
440 440 def depart_caption(self, node):
441 441 pass
442 442
443 443 def visit_caution(self, node):
444 444 self.visit_admonition(node, 'caution')
445 445
446 446 depart_caution = depart_admonition
447 447
448 448 def visit_citation(self, node):
449 449 num, text = node.astext().split(None, 1)
450 450 num = num.strip()
451 451 self.body.append('.IP [%s] 5\n' % num)
452 452
453 453 def depart_citation(self, node):
454 454 pass
455 455
456 456 def visit_citation_reference(self, node):
457 457 self.body.append('['+node.astext()+']')
458 458 raise nodes.SkipNode
459 459
460 460 def visit_classifier(self, node):
461 461 pass
462 462
463 463 def depart_classifier(self, node):
464 464 pass
465 465
466 466 def visit_colspec(self, node):
467 467 self.colspecs.append(node)
468 468
469 469 def depart_colspec(self, node):
470 470 pass
471 471
472 472 def write_colspecs(self):
473 473 self.body.append("%s.\n" % ('L '*len(self.colspecs)))
474 474
475 475 def visit_comment(self, node,
476 476 sub=re.compile('-(?=-)').sub):
477 477 self.body.append(self.comment(node.astext()))
478 478 raise nodes.SkipNode
479 479
480 480 def visit_contact(self, node):
481 481 self.visit_docinfo_item(node, 'contact')
482 482
483 483 depart_contact = depart_docinfo_item
484 484
485 485 def visit_container(self, node):
486 486 pass
487 487
488 488 def depart_container(self, node):
489 489 pass
490 490
491 491 def visit_compound(self, node):
492 492 pass
493 493
494 494 def depart_compound(self, node):
495 495 pass
496 496
497 497 def visit_copyright(self, node):
498 498 self.visit_docinfo_item(node, 'copyright')
499 499
500 500 def visit_danger(self, node):
501 501 self.visit_admonition(node, 'danger')
502 502
503 503 depart_danger = depart_admonition
504 504
505 505 def visit_date(self, node):
506 506 self.visit_docinfo_item(node, 'date')
507 507
508 508 def visit_decoration(self, node):
509 509 pass
510 510
511 511 def depart_decoration(self, node):
512 512 pass
513 513
514 514 def visit_definition(self, node):
515 515 pass
516 516
517 517 def depart_definition(self, node):
518 518 pass
519 519
520 520 def visit_definition_list(self, node):
521 521 self.indent(DEFINITION_LIST_INDENT)
522 522
523 523 def depart_definition_list(self, node):
524 524 self.dedent()
525 525
526 526 def visit_definition_list_item(self, node):
527 527 self.body.append(self.defs['definition_list_item'][0])
528 528
529 529 def depart_definition_list_item(self, node):
530 530 self.body.append(self.defs['definition_list_item'][1])
531 531
532 532 def visit_description(self, node):
533 533 pass
534 534
535 535 def depart_description(self, node):
536 536 pass
537 537
538 538 def visit_docinfo(self, node):
539 539 self._in_docinfo = 1
540 540
541 541 def depart_docinfo(self, node):
542 542 self._in_docinfo = None
543 543 # NOTE nothing should be written before this
544 544 self.append_header()
545 545
546 546 def visit_doctest_block(self, node):
547 547 self.body.append(self.defs['literal_block'][0])
548 548 self._in_literal = True
549 549
550 550 def depart_doctest_block(self, node):
551 551 self._in_literal = False
552 552 self.body.append(self.defs['literal_block'][1])
553 553
554 554 def visit_document(self, node):
555 555 # no blank line between comment and header.
556 556 self.body.append(self.comment(self.document_start).rstrip()+'\n')
557 557 # writing header is postboned
558 558 self.header_written = 0
559 559
560 560 def depart_document(self, node):
561 561 if self._docinfo['author']:
562 562 self.body.append('.SH AUTHOR\n%s\n'
563 563 % ', '.join(self._docinfo['author']))
564 564 skip = ('author', 'copyright', 'date',
565 565 'manual_group', 'manual_section',
566 566 'subtitle',
567 567 'title', 'title_upper', 'version')
568 568 for name in self._docinfo_keys:
569 569 if name == 'address':
570 570 self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % (
571 571 self.language.labels.get(name, name),
572 572 self.defs['indent'][0] % 0,
573 573 self.defs['indent'][0] % BLOCKQOUTE_INDENT,
574 574 self._docinfo[name],
575 575 self.defs['indent'][1],
576 576 self.defs['indent'][1]))
577 577 elif not name in skip:
578 578 if name in self._docinfo_names:
579 579 label = self._docinfo_names[name]
580 580 else:
581 581 label = self.language.labels.get(name, name)
582 582 self.body.append("\n%s: %s\n" % (label, self._docinfo[name]))
583 583 if self._docinfo['copyright']:
584 584 self.body.append('.SH COPYRIGHT\n%s\n'
585 585 % self._docinfo['copyright'])
586 586 self.body.append(self.comment(
587 587 'Generated by docutils manpage writer.\n'))
588 588
589 589 def visit_emphasis(self, node):
590 590 self.body.append(self.defs['emphasis'][0])
591 591
592 592 def depart_emphasis(self, node):
593 593 self.body.append(self.defs['emphasis'][1])
594 594
595 595 def visit_entry(self, node):
596 596 # a cell in a table row
597 597 if 'morerows' in node:
598 598 self.document.reporter.warning('"table row spanning" not supported',
599 599 base_node=node)
600 600 if 'morecols' in node:
601 601 self.document.reporter.warning(
602 602 '"table cell spanning" not supported', base_node=node)
603 603 self.context.append(len(self.body))
604 604
605 605 def depart_entry(self, node):
606 606 start = self.context.pop()
607 607 self._active_table.append_cell(self.body[start:])
608 608 del self.body[start:]
609 609
610 610 def visit_enumerated_list(self, node):
611 611 self.list_start(node)
612 612
613 613 def depart_enumerated_list(self, node):
614 614 self.list_end()
615 615
616 616 def visit_error(self, node):
617 617 self.visit_admonition(node, 'error')
618 618
619 619 depart_error = depart_admonition
620 620
621 621 def visit_field(self, node):
622 622 pass
623 623
624 624 def depart_field(self, node):
625 625 pass
626 626
627 627 def visit_field_body(self, node):
628 628 if self._in_docinfo:
629 629 name_normalized = self._field_name.lower().replace(" ","_")
630 630 self._docinfo_names[name_normalized] = self._field_name
631 631 self.visit_docinfo_item(node, name_normalized)
632 632 raise nodes.SkipNode
633 633
634 634 def depart_field_body(self, node):
635 635 pass
636 636
637 637 def visit_field_list(self, node):
638 638 self.indent(FIELD_LIST_INDENT)
639 639
640 640 def depart_field_list(self, node):
641 641 self.dedent()
642 642
643 643 def visit_field_name(self, node):
644 644 if self._in_docinfo:
645 645 self._field_name = node.astext()
646 646 raise nodes.SkipNode
647 647 else:
648 648 self.body.append(self.defs['field_name'][0])
649 649
650 650 def depart_field_name(self, node):
651 651 self.body.append(self.defs['field_name'][1])
652 652
653 653 def visit_figure(self, node):
654 654 self.indent(2.5)
655 655 self.indent(0)
656 656
657 657 def depart_figure(self, node):
658 658 self.dedent()
659 659 self.dedent()
660 660
661 661 def visit_footer(self, node):
662 662 self.document.reporter.warning('"footer" not supported',
663 663 base_node=node)
664 664
665 665 def depart_footer(self, node):
666 666 pass
667 667
668 668 def visit_footnote(self, node):
669 669 num, text = node.astext().split(None, 1)
670 670 num = num.strip()
671 671 self.body.append('.IP [%s] 5\n' % self.deunicode(num))
672 672
673 673 def depart_footnote(self, node):
674 674 pass
675 675
676 676 def footnote_backrefs(self, node):
677 677 self.document.reporter.warning('"footnote_backrefs" not supported',
678 678 base_node=node)
679 679
680 680 def visit_footnote_reference(self, node):
681 681 self.body.append('['+self.deunicode(node.astext())+']')
682 682 raise nodes.SkipNode
683 683
684 684 def depart_footnote_reference(self, node):
685 685 pass
686 686
687 687 def visit_generated(self, node):
688 688 pass
689 689
690 690 def depart_generated(self, node):
691 691 pass
692 692
693 693 def visit_header(self, node):
694 694 raise NotImplementedError, node.astext()
695 695
696 696 def depart_header(self, node):
697 697 pass
698 698
699 699 def visit_hint(self, node):
700 700 self.visit_admonition(node, 'hint')
701 701
702 702 depart_hint = depart_admonition
703 703
704 704 def visit_subscript(self, node):
705 705 self.body.append('\\s-2\\d')
706 706
707 707 def depart_subscript(self, node):
708 708 self.body.append('\\u\\s0')
709 709
710 710 def visit_superscript(self, node):
711 711 self.body.append('\\s-2\\u')
712 712
713 713 def depart_superscript(self, node):
714 714 self.body.append('\\d\\s0')
715 715
716 716 def visit_attribution(self, node):
717 717 self.body.append('\\(em ')
718 718
719 719 def depart_attribution(self, node):
720 720 self.body.append('\n')
721 721
722 722 def visit_image(self, node):
723 723 self.document.reporter.warning('"image" not supported',
724 724 base_node=node)
725 725 text = []
726 726 if 'alt' in node.attributes:
727 727 text.append(node.attributes['alt'])
728 728 if 'uri' in node.attributes:
729 729 text.append(node.attributes['uri'])
730 730 self.body.append('[image: %s]\n' % ('/'.join(text)))
731 731 raise nodes.SkipNode
732 732
733 733 def visit_important(self, node):
734 734 self.visit_admonition(node, 'important')
735 735
736 736 depart_important = depart_admonition
737 737
738 738 def visit_label(self, node):
739 739 # footnote and citation
740 740 if (isinstance(node.parent, nodes.footnote)
741 741 or isinstance(node.parent, nodes.citation)):
742 742 raise nodes.SkipNode
743 743 self.document.reporter.warning('"unsupported "label"',
744 744 base_node=node)
745 745 self.body.append('[')
746 746
747 747 def depart_label(self, node):
748 748 self.body.append(']\n')
749 749
750 750 def visit_legend(self, node):
751 751 pass
752 752
753 753 def depart_legend(self, node):
754 754 pass
755 755
756 756 # WHAT should we use .INDENT, .UNINDENT ?
757 757 def visit_line_block(self, node):
758 758 self._line_block += 1
759 759 if self._line_block == 1:
760 760 self.body.append('.sp\n')
761 761 self.body.append('.nf\n')
762 762 else:
763 763 self.body.append('.in +2\n')
764 764
765 765 def depart_line_block(self, node):
766 766 self._line_block -= 1
767 767 if self._line_block == 0:
768 768 self.body.append('.fi\n')
769 769 self.body.append('.sp\n')
770 770 else:
771 771 self.body.append('.in -2\n')
772 772
773 773 def visit_line(self, node):
774 774 pass
775 775
776 776 def depart_line(self, node):
777 777 self.body.append('\n')
778 778
779 779 def visit_list_item(self, node):
780 780 # man 7 man argues to use ".IP" instead of ".TP"
781 781 self.body.append('.IP %s %d\n' % (
782 782 self._list_char[-1].next(),
783 783 self._list_char[-1].get_width(),))
784 784
785 785 def depart_list_item(self, node):
786 786 pass
787 787
788 788 def visit_literal(self, node):
789 789 self.body.append(self.defs['literal'][0])
790 790
791 791 def depart_literal(self, node):
792 792 self.body.append(self.defs['literal'][1])
793 793
794 794 def visit_literal_block(self, node):
795 795 self.body.append(self.defs['literal_block'][0])
796 796 self._in_literal = True
797 797
798 798 def depart_literal_block(self, node):
799 799 self._in_literal = False
800 800 self.body.append(self.defs['literal_block'][1])
801 801
802 802 def visit_meta(self, node):
803 803 raise NotImplementedError, node.astext()
804 804
805 805 def depart_meta(self, node):
806 806 pass
807 807
808 808 def visit_note(self, node):
809 809 self.visit_admonition(node, 'note')
810 810
811 811 depart_note = depart_admonition
812 812
813 813 def indent(self, by=0.5):
814 814 # if we are in a section ".SH" there already is a .RS
815 815 step = self._indent[-1]
816 816 self._indent.append(by)
817 817 self.body.append(self.defs['indent'][0] % step)
818 818
819 819 def dedent(self):
820 820 self._indent.pop()
821 821 self.body.append(self.defs['indent'][1])
822 822
823 823 def visit_option_list(self, node):
824 824 self.indent(OPTION_LIST_INDENT)
825 825
826 826 def depart_option_list(self, node):
827 827 self.dedent()
828 828
829 829 def visit_option_list_item(self, node):
830 830 # one item of the list
831 831 self.body.append(self.defs['option_list_item'][0])
832 832
833 833 def depart_option_list_item(self, node):
834 834 self.body.append(self.defs['option_list_item'][1])
835 835
836 836 def visit_option_group(self, node):
837 837 # as one option could have several forms it is a group
838 838 # options without parameter bold only, .B, -v
839 839 # options with parameter bold italic, .BI, -f file
840 840 #
841 841 # we do not know if .B or .BI
842 842 self.context.append('.B') # blind guess
843 843 self.context.append(len(self.body)) # to be able to insert later
844 844 self.context.append(0) # option counter
845 845
846 846 def depart_option_group(self, node):
847 847 self.context.pop() # the counter
848 848 start_position = self.context.pop()
849 849 text = self.body[start_position:]
850 850 del self.body[start_position:]
851 851 self.body.append('%s%s\n' % (self.context.pop(), ''.join(text)))
852 852
853 853 def visit_option(self, node):
854 854 # each form of the option will be presented separately
855 855 if self.context[-1] > 0:
856 856 self.body.append(', ')
857 857 if self.context[-3] == '.BI':
858 858 self.body.append('\\')
859 859 self.body.append(' ')
860 860
861 861 def depart_option(self, node):
862 862 self.context[-1] += 1
863 863
864 864 def visit_option_string(self, node):
865 865 # do not know if .B or .BI
866 866 pass
867 867
868 868 def depart_option_string(self, node):
869 869 pass
870 870
871 871 def visit_option_argument(self, node):
872 872 self.context[-3] = '.BI' # bold/italic alternate
873 873 if node['delimiter'] != ' ':
874 874 self.body.append('\\fB%s ' % node['delimiter'])
875 875 elif self.body[len(self.body)-1].endswith('='):
876 876 # a blank only means no blank in output, just changing font
877 877 self.body.append(' ')
878 878 else:
879 879 # blank backslash blank, switch font then a blank
880 880 self.body.append(' \\ ')
881 881
882 882 def depart_option_argument(self, node):
883 883 pass
884 884
885 885 def visit_organization(self, node):
886 886 self.visit_docinfo_item(node, 'organization')
887 887
888 888 def depart_organization(self, node):
889 889 pass
890 890
891 891 def visit_paragraph(self, node):
892 892 # ``.PP`` : Start standard indented paragraph.
893 893 # ``.LP`` : Start block paragraph, all except the first.
894 894 # ``.P [type]`` : Start paragraph type.
895 895 # NOTE dont use paragraph starts because they reset indentation.
896 896 # ``.sp`` is only vertical space
897 897 self.ensure_eol()
898 898 self.body.append('.sp\n')
899 899
900 900 def depart_paragraph(self, node):
901 901 self.body.append('\n')
902 902
903 903 def visit_problematic(self, node):
904 904 self.body.append(self.defs['problematic'][0])
905 905
906 906 def depart_problematic(self, node):
907 907 self.body.append(self.defs['problematic'][1])
908 908
909 909 def visit_raw(self, node):
910 910 if node.get('format') == 'manpage':
911 911 self.body.append(node.astext() + "\n")
912 912 # Keep non-manpage raw text out of output:
913 913 raise nodes.SkipNode
914 914
915 915 def visit_reference(self, node):
916 916 """E.g. link or email address."""
917 917 self.body.append(self.defs['reference'][0])
918 918
919 919 def depart_reference(self, node):
920 920 self.body.append(self.defs['reference'][1])
921 921
922 922 def visit_revision(self, node):
923 923 self.visit_docinfo_item(node, 'revision')
924 924
925 925 depart_revision = depart_docinfo_item
926 926
927 927 def visit_row(self, node):
928 928 self._active_table.new_row()
929 929
930 930 def depart_row(self, node):
931 931 pass
932 932
933 933 def visit_section(self, node):
934 934 self.section_level += 1
935 935
936 936 def depart_section(self, node):
937 937 self.section_level -= 1
938 938
939 939 def visit_status(self, node):
940 940 self.visit_docinfo_item(node, 'status')
941 941
942 942 depart_status = depart_docinfo_item
943 943
944 944 def visit_strong(self, node):
945 945 self.body.append(self.defs['strong'][0])
946 946
947 947 def depart_strong(self, node):
948 948 self.body.append(self.defs['strong'][1])
949 949
950 950 def visit_substitution_definition(self, node):
951 951 """Internal only."""
952 952 raise nodes.SkipNode
953 953
954 954 def visit_substitution_reference(self, node):
955 955 self.document.reporter.warning('"substitution_reference" not supported',
956 956 base_node=node)
957 957
958 958 def visit_subtitle(self, node):
959 959 if isinstance(node.parent, nodes.sidebar):
960 960 self.body.append(self.defs['strong'][0])
961 961 elif isinstance(node.parent, nodes.document):
962 962 self.visit_docinfo_item(node, 'subtitle')
963 963 elif isinstance(node.parent, nodes.section):
964 964 self.body.append(self.defs['strong'][0])
965 965
966 966 def depart_subtitle(self, node):
967 967 # document subtitle calls SkipNode
968 968 self.body.append(self.defs['strong'][1]+'\n.PP\n')
969 969
970 970 def visit_system_message(self, node):
971 971 # TODO add report_level
972 972 #if node['level'] < self.document.reporter['writer'].report_level:
973 973 # Level is too low to display:
974 974 # raise nodes.SkipNode
975 975 attr = {}
976 976 backref_text = ''
977 977 if node.hasattr('id'):
978 978 attr['name'] = node['id']
979 979 if node.hasattr('line'):
980 980 line = ', line %s' % node['line']
981 981 else:
982 982 line = ''
983 983 self.body.append('.IP "System Message: %s/%s (%s:%s)"\n'
984 984 % (node['type'], node['level'], node['source'], line))
985 985
986 986 def depart_system_message(self, node):
987 987 pass
988 988
989 989 def visit_table(self, node):
990 990 self._active_table = Table()
991 991
992 992 def depart_table(self, node):
993 993 self.ensure_eol()
994 994 self.body.extend(self._active_table.as_list())
995 995 self._active_table = None
996 996
997 997 def visit_target(self, node):
998 998 # targets are in-document hyper targets, without any use for man-pages.
999 999 raise nodes.SkipNode
1000 1000
1001 1001 def visit_tbody(self, node):
1002 1002 pass
1003 1003
1004 1004 def depart_tbody(self, node):
1005 1005 pass
1006 1006
1007 1007 def visit_term(self, node):
1008 1008 self.body.append(self.defs['term'][0])
1009 1009
1010 1010 def depart_term(self, node):
1011 1011 self.body.append(self.defs['term'][1])
1012 1012
1013 1013 def visit_tgroup(self, node):
1014 1014 pass
1015 1015
1016 1016 def depart_tgroup(self, node):
1017 1017 pass
1018 1018
1019 1019 def visit_thead(self, node):
1020 1020 # MAYBE double line '='
1021 1021 pass
1022 1022
1023 1023 def depart_thead(self, node):
1024 1024 # MAYBE double line '='
1025 1025 pass
1026 1026
1027 1027 def visit_tip(self, node):
1028 1028 self.visit_admonition(node, 'tip')
1029 1029
1030 1030 depart_tip = depart_admonition
1031 1031
1032 1032 def visit_title(self, node):
1033 1033 if isinstance(node.parent, nodes.topic):
1034 1034 self.body.append(self.defs['topic-title'][0])
1035 1035 elif isinstance(node.parent, nodes.sidebar):
1036 1036 self.body.append(self.defs['sidebar-title'][0])
1037 1037 elif isinstance(node.parent, nodes.admonition):
1038 1038 self.body.append('.IP "')
1039 1039 elif self.section_level == 0:
1040 1040 self._docinfo['title'] = node.astext()
1041 1041 # document title for .TH
1042 1042 self._docinfo['title_upper'] = node.astext().upper()
1043 1043 raise nodes.SkipNode
1044 1044 elif self.section_level == 1:
1045 1045 self.body.append('.SH ')
1046 1046 for n in node.traverse(nodes.Text):
1047 1047 n.parent.replace(n, nodes.Text(n.astext().upper()))
1048 1048 else:
1049 1049 self.body.append('.SS ')
1050 1050
1051 1051 def depart_title(self, node):
1052 1052 if isinstance(node.parent, nodes.admonition):
1053 1053 self.body.append('"')
1054 1054 self.body.append('\n')
1055 1055
1056 1056 def visit_title_reference(self, node):
1057 1057 """inline citation reference"""
1058 1058 self.body.append(self.defs['title_reference'][0])
1059 1059
1060 1060 def depart_title_reference(self, node):
1061 1061 self.body.append(self.defs['title_reference'][1])
1062 1062
1063 1063 def visit_topic(self, node):
1064 1064 pass
1065 1065
1066 1066 def depart_topic(self, node):
1067 1067 pass
1068 1068
1069 1069 def visit_sidebar(self, node):
1070 1070 pass
1071 1071
1072 1072 def depart_sidebar(self, node):
1073 1073 pass
1074 1074
1075 1075 def visit_rubric(self, node):
1076 1076 pass
1077 1077
1078 1078 def depart_rubric(self, node):
1079 1079 pass
1080 1080
1081 1081 def visit_transition(self, node):
1082 1082 # .PP Begin a new paragraph and reset prevailing indent.
1083 1083 # .sp N leaves N lines of blank space.
1084 1084 # .ce centers the next line
1085 1085 self.body.append('\n.sp\n.ce\n----\n')
1086 1086
1087 1087 def depart_transition(self, node):
1088 1088 self.body.append('\n.ce 0\n.sp\n')
1089 1089
1090 1090 def visit_version(self, node):
1091 1091 self.visit_docinfo_item(node, 'version')
1092 1092
1093 1093 def visit_warning(self, node):
1094 1094 self.visit_admonition(node, 'warning')
1095 1095
1096 1096 depart_warning = depart_admonition
1097 1097
1098 1098 def unimplemented_visit(self, node):
1099 1099 raise NotImplementedError('visiting unimplemented node type: %s'
1100 1100 % node.__class__.__name__)
1101 1101
1102 # The following part is taken from the Docutils rst2man.py script:
1103 if __name__ == "__main__":
1104 from docutils.core import publish_cmdline, default_description
1105 description = ("Generates plain unix manual documents. " +
1106 default_description)
1107 publish_cmdline(writer=Writer(), description=description)
1108
1109 1102 # vim: set fileencoding=utf-8 et ts=4 ai :
@@ -1,276 +1,276 b''
1 1 #!/usr/bin/env python
2 2 """Test the running system for features availability. Exit with zero
3 3 if all features are there, non-zero otherwise. If a feature name is
4 4 prefixed with "no-", the absence of feature is tested.
5 5 """
6 6 import optparse
7 7 import os
8 8 import re
9 9 import sys
10 10 import tempfile
11 11
12 12 tempprefix = 'hg-hghave-'
13 13
14 14 def matchoutput(cmd, regexp, ignorestatus=False):
15 15 """Return True if cmd executes successfully and its output
16 16 is matched by the supplied regular expression.
17 17 """
18 18 r = re.compile(regexp)
19 19 fh = os.popen(cmd)
20 20 s = fh.read()
21 21 try:
22 22 ret = fh.close()
23 23 except IOError:
24 24 # Happen in Windows test environment
25 25 ret = 1
26 26 return (ignorestatus or ret is None) and r.search(s)
27 27
28 28 def has_baz():
29 29 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
30 30
31 31 def has_bzr():
32 32 try:
33 33 import bzrlib
34 34 return bzrlib.__doc__ != None
35 35 except ImportError:
36 36 return False
37 37
38 38 def has_bzr114():
39 39 try:
40 40 import bzrlib
41 41 return (bzrlib.__doc__ != None
42 42 and bzrlib.version_info[:2] >= (1, 14))
43 43 except ImportError:
44 44 return False
45 45
46 46 def has_cvs():
47 47 re = r'Concurrent Versions System.*?server'
48 48 return matchoutput('cvs --version 2>&1', re)
49 49
50 50 def has_darcs():
51 51 return matchoutput('darcs --version', r'2\.[2-9]', True)
52 52
53 53 def has_mtn():
54 54 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
55 55 'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
56 56
57 57 def has_eol_in_paths():
58 58 try:
59 59 fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r')
60 60 os.close(fd)
61 61 os.remove(path)
62 62 return True
63 63 except:
64 64 return False
65 65
66 66 def has_executablebit():
67 67 fd, path = tempfile.mkstemp(prefix=tempprefix)
68 68 os.close(fd)
69 69 try:
70 70 s = os.lstat(path).st_mode
71 71 os.chmod(path, s | 0100)
72 72 return (os.lstat(path).st_mode & 0100 != 0)
73 73 finally:
74 74 os.remove(path)
75 75
76 76 def has_icasefs():
77 77 # Stolen from mercurial.util
78 78 fd, path = tempfile.mkstemp(prefix=tempprefix, dir='.')
79 79 os.close(fd)
80 80 try:
81 81 s1 = os.stat(path)
82 82 d, b = os.path.split(path)
83 83 p2 = os.path.join(d, b.upper())
84 84 if path == p2:
85 85 p2 = os.path.join(d, b.lower())
86 86 try:
87 87 s2 = os.stat(p2)
88 88 return s2 == s1
89 89 except:
90 90 return False
91 91 finally:
92 92 os.remove(path)
93 93
94 94 def has_inotify():
95 95 try:
96 96 import hgext.inotify.linux.watcher
97 97 return True
98 98 except ImportError:
99 99 return False
100 100
101 101 def has_fifo():
102 102 return hasattr(os, "mkfifo")
103 103
104 104 def has_hotshot():
105 105 try:
106 106 # hotshot.stats tests hotshot and many problematic dependencies
107 107 # like profile.
108 108 import hotshot.stats
109 109 return True
110 110 except ImportError:
111 111 return False
112 112
113 113 def has_lsprof():
114 114 try:
115 115 import _lsprof
116 116 return True
117 117 except ImportError:
118 118 return False
119 119
120 120 def has_git():
121 121 return matchoutput('git --version 2>&1', r'^git version')
122 122
123 def has_rst2html():
124 for name in ('rst2html', 'rst2html.py'):
125 name = name + ' --version 2>&1'
126 if matchoutput(name, r'^rst2html(?:\.py)? \(Docutils'):
127 return True
128 return False
123 def has_docutils():
124 try:
125 from docutils.core import publish_cmdline
126 return True
127 except ImportError:
128 return False
129 129
130 130 def has_svn():
131 131 return matchoutput('svn --version 2>&1', r'^svn, version') and \
132 132 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
133 133
134 134 def has_svn_bindings():
135 135 try:
136 136 import svn.core
137 137 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
138 138 if version < (1, 4):
139 139 return False
140 140 return True
141 141 except ImportError:
142 142 return False
143 143
144 144 def has_p4():
145 145 return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/')
146 146
147 147 def has_symlink():
148 148 return hasattr(os, "symlink")
149 149
150 150 def has_tla():
151 151 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
152 152
153 153 def has_gpg():
154 154 return matchoutput('gpg --version 2>&1', r'GnuPG')
155 155
156 156 def has_unix_permissions():
157 157 d = tempfile.mkdtemp(prefix=tempprefix, dir=".")
158 158 try:
159 159 fname = os.path.join(d, 'foo')
160 160 for umask in (077, 007, 022):
161 161 os.umask(umask)
162 162 f = open(fname, 'w')
163 163 f.close()
164 164 mode = os.stat(fname).st_mode
165 165 os.unlink(fname)
166 166 if mode & 0777 != ~umask & 0666:
167 167 return False
168 168 return True
169 169 finally:
170 170 os.rmdir(d)
171 171
172 172 def has_pygments():
173 173 try:
174 174 import pygments
175 175 return True
176 176 except ImportError:
177 177 return False
178 178
179 179 def has_outer_repo():
180 180 return matchoutput('hg root 2>&1', r'')
181 181
182 182 checks = {
183 183 "baz": (has_baz, "GNU Arch baz client"),
184 184 "bzr": (has_bzr, "Canonical's Bazaar client"),
185 185 "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
186 186 "cvs": (has_cvs, "cvs client/server"),
187 187 "darcs": (has_darcs, "darcs client"),
188 188 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
189 189 "execbit": (has_executablebit, "executable bit"),
190 190 "fifo": (has_fifo, "named pipes"),
191 191 "git": (has_git, "git command line client"),
192 192 "gpg": (has_gpg, "gpg client"),
193 193 "hotshot": (has_hotshot, "python hotshot module"),
194 194 "icasefs": (has_icasefs, "case insensitive file system"),
195 195 "inotify": (has_inotify, "inotify extension support"),
196 196 "lsprof": (has_lsprof, "python lsprof module"),
197 197 "mtn": (has_mtn, "monotone client (> 0.31)"),
198 198 "outer-repo": (has_outer_repo, "outer repo"),
199 199 "p4": (has_p4, "Perforce server and client"),
200 200 "pygments": (has_pygments, "Pygments source highlighting library"),
201 "rst2html": (has_rst2html, "Docutils rst2html tool"),
201 "docutils": (has_docutils, "Docutils text processing library"),
202 202 "svn": (has_svn, "subversion client and admin tools"),
203 203 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
204 204 "symlink": (has_symlink, "symbolic links"),
205 205 "tla": (has_tla, "GNU Arch tla client"),
206 206 "unix-permissions": (has_unix_permissions, "unix-style permissions"),
207 207 }
208 208
209 209 def list_features():
210 210 for name, feature in checks.iteritems():
211 211 desc = feature[1]
212 212 print name + ':', desc
213 213
214 214 def test_features():
215 215 failed = 0
216 216 for name, feature in checks.iteritems():
217 217 check, _ = feature
218 218 try:
219 219 check()
220 220 except Exception, e:
221 221 print "feature %s failed: %s" % (name, e)
222 222 failed += 1
223 223 return failed
224 224
225 225 parser = optparse.OptionParser("%prog [options] [features]")
226 226 parser.add_option("--test-features", action="store_true",
227 227 help="test available features")
228 228 parser.add_option("--list-features", action="store_true",
229 229 help="list available features")
230 230 parser.add_option("-q", "--quiet", action="store_true",
231 231 help="check features silently")
232 232
233 233 if __name__ == '__main__':
234 234 options, args = parser.parse_args()
235 235 if options.list_features:
236 236 list_features()
237 237 sys.exit(0)
238 238
239 239 if options.test_features:
240 240 sys.exit(test_features())
241 241
242 242 quiet = options.quiet
243 243
244 244 failures = 0
245 245
246 246 def error(msg):
247 247 global failures
248 248 if not quiet:
249 249 sys.stderr.write(msg + '\n')
250 250 failures += 1
251 251
252 252 for feature in args:
253 253 negate = feature.startswith('no-')
254 254 if negate:
255 255 feature = feature[3:]
256 256
257 257 if feature not in checks:
258 258 error('skipped: unknown feature: ' + feature)
259 259 continue
260 260
261 261 check, desc = checks[feature]
262 262 try:
263 263 available = check()
264 264 except Exception, e:
265 265 error('hghave check failed: ' + feature)
266 266 continue
267 267
268 268 if not negate and not available:
269 269 error('skipped: missing feature: ' + desc)
270 270 elif negate and available:
271 271 error('skipped: system supports %s' % desc)
272 272
273 273 if failures != 0:
274 274 sys.exit(1)
275 275
276 276
@@ -1,21 +1,20 b''
1 1 #!/bin/sh
2 2
3 "$TESTDIR/hghave" rst2html || exit 80
4 RST2HTML=`which rst2html 2> /dev/null || which rst2html.py`
3 "$TESTDIR/hghave" docutils || exit 80
5 4
6 5 HGENCODING=UTF-8
7 6 export HGENCODING
8 7
9 8 for PO in C $TESTDIR/../i18n/*.po; do
10 9 LOCALE=`basename $PO .po`
11 10 echo
12 11 echo "% extracting documentation from $LOCALE"
13 12 echo ".. -*- coding: utf-8 -*-" > gendoc-$LOCALE.txt
14 13 echo "" >> gendoc-$LOCALE.txt
15 14 LC_ALL=$LOCALE python $TESTDIR/../doc/gendoc.py >> gendoc-$LOCALE.txt || exit
16 15
17 # We run rst2html over the file without adding "--halt warning" to
18 # make it report all errors instead of stopping on the first one.
19 echo "checking for parse errors with rst2html"
20 $RST2HTML gendoc-$LOCALE.txt /dev/null
16 # We call runrst without adding "--halt warning" to make it report
17 # all errors instead of stopping on the first one.
18 echo "checking for parse errors"
19 python $TESTDIR/../doc/runrst html gendoc-$LOCALE.txt /dev/null
21 20 done
@@ -1,33 +1,33 b''
1 1
2 2 % extracting documentation from C
3 checking for parse errors with rst2html
3 checking for parse errors
4 4
5 5 % extracting documentation from da
6 checking for parse errors with rst2html
6 checking for parse errors
7 7
8 8 % extracting documentation from de
9 checking for parse errors with rst2html
9 checking for parse errors
10 10
11 11 % extracting documentation from el
12 checking for parse errors with rst2html
12 checking for parse errors
13 13
14 14 % extracting documentation from fr
15 checking for parse errors with rst2html
15 checking for parse errors
16 16
17 17 % extracting documentation from it
18 checking for parse errors with rst2html
18 checking for parse errors
19 19
20 20 % extracting documentation from ja
21 checking for parse errors with rst2html
21 checking for parse errors
22 22
23 23 % extracting documentation from pt_BR
24 checking for parse errors with rst2html
24 checking for parse errors
25 25
26 26 % extracting documentation from sv
27 checking for parse errors with rst2html
27 checking for parse errors
28 28
29 29 % extracting documentation from zh_CN
30 checking for parse errors with rst2html
30 checking for parse errors
31 31
32 32 % extracting documentation from zh_TW
33 checking for parse errors with rst2html
33 checking for parse errors
General Comments 0
You need to be logged in to leave comments. Login now