File: build_doc.py

package info (click to toggle)
pyopengl 2.0.1.08-5.1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 19,484 kB
  • ctags: 9,036
  • sloc: pascal: 64,950; xml: 28,088; ansic: 20,696; python: 19,761; tcl: 668; makefile: 240; sh: 25
file content (398 lines) | stat: -rwxr-xr-x 13,698 bytes parent folder | download | duplicates (2)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#!/usr/bin/env python
"""Builds different documentation formats from docbook src

Here's the basic approach:
	A driver file defines system entities point to source-files
		(manual.xml)
	On reading the driver file, the parser pulls together all
	of the source-files into one big document.

	For the reference-manual, we merge in Python-specific
	annotations from a seperate directory tree with a (slow)
	merge.xsl template.

	Once we have the merged file, we can use the docbook-xsl
	templates to generate html, htmlhelp, or xhtml documents.
	Apparently we can also use latex/pdflatex if we have them
	to process this docbook file.

	For HTML files, we run David Carlisle' mathml XSL
	stylesheets in stand-alone mode to create IE-compatible
	HTML (with Javascript).

Note:
	This process is slow, even with Saxon (which is fairly
	fast).  It's untenably slow with the 4Suite XSLT package.

XXX Plans/desires:

	Eventually, I want to break the processing up into multiple
	distutils commands to allow for explicitly specifying which
	commands should be run, rather than having a single channel
	which decides which parts of itself to run:

		doc_merge_python -- merge individual sections with python
			annotations into combined document fragments
		doc_collect -- collect all individual section document
			fragments into a single large manual document
		doc_merge_samples -- add sample sections to the manual
		doc_build_xhtml -- use docbook-xsl to generate xHTML files
		doc_build_html -- convert xhtml to html
			doc_convert_mathml -- process html to make the html files
				work under IE 5 and 6
		doc_build_htmlhelp -- use docbook-xsl to generate HTMLHelp
			files
			doc_convert_mathml -- process html to make the html files
				work under IE 5 and 6

	and make the individual stages capable of optimizing in
	such a way that changing a particular python-specific
	document will only require merging that particular document
	and integrating it into the already-built manual, then
	running the appropriate doc_build_XXX commands. This will
	require completely rewriting the merge.xsl style sheet,
	and the driver code for manipulating it, as well as the
	build directory structure for holding the temporary files.

	I would also like to parameterize most of the functions
	so that we can generate/process the manual in non-standard
	places (to allow for, for instance, a special doc_build_html
	run which uses doc_merge_samples to not overwrite the
	standard manual).

	Should also eliminate the practice of directly writing to the
	OpenGL/doc directory during building, as this will cause
	problems if later build procedures fail.  Should generate the
	documents entirely within the build tree, and only copy them on
	complete success.
"""
import sys, os, os.path, re, string
from distutils.filelist import FileList
from distutils.errors import *
from util import *
import distutils.cmd
import glob, shutil

def show_formats ():
	"""Print all possible values for the 'formats' option (used by
	the "--help-formats" command-line option).
	"""
	from distutils.fancy_getopt import FancyGetopt
	formats=['html', 'xhtml', 'htmlhelp']
	formats.sort()
#	pretty_printer = FancyGetopt(formats)
#	pretty_printer.print_help(
#		"List of available source distribution formats:")


code_re = re.compile('&#([0-9]+);')

MATHPLAYER_BOILERPLATE = """
<object id="MathPlayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987"></object>
<?import namespace="mml" IMPLEMENTATION="#MathPlayer" ?>
"""

CUSTOM_XSL = os.path.join("doc","xsl","html")
DOCBOOK_XSL_HOME = os.path.join("doc","docbook-xsl")
DOCBOOK_DTD_HOME = os.path.join("doc","docbook")
MATHML_CSS_HOME = os.path.join("doc","xsl","mathml")


class build_doc(distutils.cmd.Command):
	"""Build HTML/XHTML documentation for PyOpenGL"""
	description = '"build" documentation.'

	user_options = [
		('force', 'f', "forcibly build everything (ignore file timestamps)"),
		('formats=', None, 'Formats for documentation'),
		('kaffe', 'k', 'Use Kaffe instead of Java'),
		]

	boolean_options = ['force']

	help_options = [
		(
			'help-formats', None,
			'list available distribution formats', show_formats
		),
	]
	def initialize_options (self):
		self.force = None
		self.formats = None
		self.kaffe = None
	def finalize_options (self):
		self.set_undefined_options('build', ('force', 'force'))
		self.ensure_string_list('formats')
		if self.formats is None:
			self.formats = ['html']
		if 'all' in self.formats:
			self.formats = ['html', 'xhtml', 'web', 'htmlhelp']
		if self.kaffe:
			self.java = 'kaffe'
			self.acp = '-addclasspath'
		else:
			self.java = 'java'
			self.acp = '-cp'

	### Overall driver for process
	def run(self):
		"""The primary driver for the documentation-building system
		"""
		## Build our Java class-path value
		paths = []
		for (variable,jarName, packageName) in [
			("SAXON_HOME", "saxon.jar", "Saxon XSL Processor"),
			("RESOLVER_HOME","resolver.jar", "Sun Resolver XML Package"),
		]:
			if not os.environ.has_key(variable):
				self.warn( """Could not find %(variable)s environmental variable.
Install %(packageName)s and set environmental variable %(variable)s to point
to the directory holding %(jarName)s.
Aborting documentation generation."""%locals() )
				return
			paths.append( os.path.join(os.environ[variable], jarName) )
		# Add the docbook-xsl extensions
		os.path.join(DOCBOOK_XSL_HOME, 'extensions', 'saxon643.jar')
		for path in paths:
			if not os.path.isfile( path ):
				self.warn( """The class-path value %s does not appear to be a valid file, errors may result.  Please check environmental variables."""%(path,) )
		# Following puts the CatalogManager.properties file on the
		# path, though that doesn't seem to actually help much in
		# reducing reliance on networked files :( 
		paths.append( DOCBOOK_DTD_HOME )
		self.cp = string.join( paths, os.pathsep )

		f = FileList()
		f.findall('doc')
		f.process_template_line('global-include *.xml')
		manualTemp = os.path.join('build', 'doc', 'manual.xml')
		self.make_file(
			f.files,
			manualTemp,
			self.merge_doc,
			(
				os.path.join('doc', 'manual', 'manual.xml'),
				manualTemp,
			),
			exec_msg = 'Merging manpages and Python-specific documentation',
			skip_msg = 'Manpages already merged with Python-specific documentation'
		)

		for format in self.formats:
			name = 'run_' + format
			if hasattr(self, name):
				self.announce( """Building format %s"""%( format,))
				getattr(self, name)()	
			else:
				self.announce( """No support available for format %s"""%( format,))

	### Pre-processing to generate the docbook document from which all formats are generated
	def merge_doc(self, source, destination, **arguments ):
		"""Merge reference document with Python-specific annotations"""
		arguments['output'] = destination
		arguments['PyOpenGL_version']= self.distribution.get_version()
		rValue = apply(
			self.saxon, (
				os.path.join('doc', 'xsl', 'merge.xsl'),
				source,
			),
			arguments
		)
		# copy so that we have a backup if the later processing
		# mucks up the merged document...
		self.copy_file(destination, os.path.join('OpenGL','doc'))
		return rValue

	### Format-specific drivers
	def run_html(self):
		"""Top-level command for generating HTML documentation"""
		self.build_xhtml('.html', os.path.join('OpenGL','doc','html'), self.fix_html_mathmlcss)


	def run_xhtml(self):
		"""Top-level command for generating XHTML documentation"""
		self.build_xhtml('.xml', os.path.join('OpenGL','doc','xhtml'), self.fix_xhtml_ns)


	def gen_xhtml(self, ext, base_dir):
		"""Generate xhtml documentation to base_dir with file extensions ext"""
		try:
			self.saxon(
				os.path.join('doc', 'xsl', 'html', 'xhtml_chunk.xsl'),
				os.path.join('build', 'doc', 'manual.xml'),
				base_dir = base_dir + '/',
				html_ext = ext
			)
		except:
			self.rmtree(base_dir)
			raise


	def build_xhtml(self, ext, base_dir, cleanupFunction=None):
		"""Build an xhtml documentation set, running cleanupFunction afterward"""
		self.make_file(
			[os.path.join('build','doc','manual.xml')],
			os.path.join(base_dir , 'index' + ext),
			self.gen_xhtml,
			(ext, base_dir)
		)
		if cleanupFunction:
			cleanupFunction(
				os.path.join( base_dir, '*' + ext )
			)
	def copy_misc( self, base_dir ):
		"""Copy miscellaneous formatting files to a base_dir"""
		self.copy_file(os.path.join('doc','misc','style.css'), base_dir)
		self.copy_file(os.path.join('doc','misc','greypinstripe.png'), base_dir)

	def run_htmlhelp(self):
		"""Generate MS HTMLHelp documentation"""
		self.make_file(['build/doc/manual.xml'],
		               'build/doc/htmlhelp/index.html',
		               self.gen_html,
		               ('build/doc/htmlhelp', 'build/doc/htmlhelp', 'htmlhelp.xsl'))

		self.mkpath('build/doc/htmlhelp')

		for file in ('manual.hhp', 'manual.hhc'):
			if os.path.exists(file):
				x = open(file).read()
##				x = string.replace(x, 'build/doc/htmlhelp_raw/', '')
				x = string.replace(x, 'build/doc/htmlhelp/', '')
				open(os.path.join('build', 'doc', 'htmlhelp', file), 'w').write(x)
				os.remove(file)
			
#		self.move_file('manual.hhp', 'build/doc/htmlhelp')
#		self.move_file('toc.hhc', 'build/doc/htmlhelp')
		
##		self.make_file(['build/doc/htmlhelp_raw/index.html'],
##		               'build/doc/htmlhelp/index.html',
##		               self.xform_html,
##		               ('build/doc/htmlhelp_raw', 'build/doc/htmlhelp'))

		self.copy_file('doc/misc/style.css', 'build/doc/htmlhelp')
		self.copy_file('doc/misc/greypinstripe.png', 'build/doc/htmlhelp')
	
		f = FileList()
		f.findall('build/doc/htmlhelp')
		f.process_template_line('global-include *')
		self.make_file(f.files,
		               'build/doc/htmlhelp/manual.chm',
		               self.hhc,
		               ())

		self.copy_file('build/doc/htmlhelp/manual.chm', 'OpenGL/doc')

	def hhc(self):
		"""Spawn the HTMLHelp compiler"""
		try:
			self.spawn(['hhc', os.path.join('build', 'doc', 'htmlhelp', 'manual.hhp')])
		except DistutilsExecError, e:
			print e
			

	def pdflatex(self):
		for i in range(3):
			self.spawn(['pdflatex', '&pdfxmltex', 'manual.xml'])


	def latex(self):
		for i in range(3):
			self.spawn(['latex', '&xmltex', 'manual.xml'])


	### Utility functions
	def saxon(self, xsl, source, output = None, **params):
		"""Run the saxon XSL processor in our environment

		Most functions wind up working through this method
		one way or another.  Basically anything which uses
		XSL transformations will call this method
		"""
		saxon_cmd = [self.java,
		             self.acp,
		             self.cp,
		             'com.icl.saxon.StyleSheet',
		             '-x',
		             'com.sun.resolver.tools.ResolvingXMLReader',
		             '-y',
		             'com.sun.resolver.tools.ResolvingXMLReader',
		             '-r',
		             'com.sun.resolver.tools.CatalogResolver']
		if output is not None:
			saxon_cmd.extend(['-o', output])
		saxon_cmd.extend([source, xsl])
		for key, value in params.items():
			saxon_cmd.append('%s=%s' % (string.replace(key, '_', '.'), value))
		self.spawn(saxon_cmd)



	def fix_xhtml_ns(self, path):
		"""Minor postprocessing to add namespace declarations for xhtml compliance"""
		self.announce('Fixing XHTML namespace attribute: %s'%(path,))
		for i in glob.glob(path):
			if os.path.isfile(i):
				x = open(i).read()
				if string.count(x, '<html>'):
					x = string.replace(x, '<html>', """<html
	xmlns="http://www.w3.org/1999/xhtml"
	xmlns:mml="http://www.w3.org/1998/Math/MathML"
>""")
					open(i, 'w').write(x)
	def fix_html_mathmlcss( self, path ):
		"""Use the MathML XSL->CSS style sheet to convert xhtml->html"""
		self.announce('Doing xhtml->html+css conversion: %s'%(path,))
		requireExtra = []
		def target( filename ):
			"""Return .html version of filename"""
			return os.path.splitext( filename )[0]+ '.html'
		for i in glob.glob(path):
			if os.path.isfile(i):
				targetName = target( i )
				x = open(i).read()
				if x.find('mml:') > -1:
					requireExtra.append( (i, targetName) )
				x = string.replace(x, '<html>', """<html
xmlns:mml="http://www.w3.org/1998/Math/MathML"
>""")
##				if x.find('mml:') > -1:
##					x = finder.sub( r'%s</head'%(MATHPLAYER_BOILERPLATE), x )
				open(targetName, 'w').write(x)
		for filename, targetName in requireExtra:
			self._fix_html_mathmlcss( targetName, targetName )
	def _fix_html_mathmlcss( self, source, target ):
		"""Do single MathML->HTML+CSS conversion"""
		self.announce('MathML Render: %s'%(source, ))
		self.saxon(
			os.path.join(MATHML_CSS_HOME, 'pmathmlcss.xsl'),
			source,
			target,
		)

	def fix_html_mathplayer(self, path):
		"""Add IE+MathPlayer specific support to HTML files (obsolete)"""
		self.announce('Adding MathPlayer support to files: %s'%(path,))
		
		finder = re.compile( "\<\/head" )
		for i in glob.glob(path):
			if os.path.isfile(i):
				x = open(i).read()
				x = string.replace(x, '<html>', """<html
xmlns:mml="http://www.w3.org/1998/Math/MathML"
>""")
				if x.find('mml:') > -1:
					x = finder.sub( r'%s</head'%(MATHPLAYER_BOILERPLATE), x )
				open(i, 'w').write(x)

	def rmtree(self, dir):
		"""Forcibly recursively remove a directory tree

		Note: this isn't cross-platform at the moment...
		"""
		try:
			shutil.rmtree(apply(os.path.join, string.split(dir, '/')))
		except:
			pass