2 # SConscript file for building SCons documentation.
8 # Permission is hereby granted, free of charge, to any person obtaining
9 # a copy of this software and associated documentation files (the
10 # "Software"), to deal in the Software without restriction, including
11 # without limitation the rights to use, copy, modify, merge, publish,
12 # distribute, sublicense, and/or sell copies of the Software, and to
13 # permit persons to whom the Software is furnished to do so, subject to
14 # the following conditions:
16 # The above copyright notice and this permission notice shall be included
17 # in all copies or substantial portions of the Software.
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
20 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
32 Import('build_dir', 'env', 'whereis')
36 build = os.path.join(build_dir, 'doc')
41 dist_doc_tar_gz = '$DISTDIR/scons-doc-${VERSION}.tar.gz'
44 # We'll only try to build text files (for some documents)
45 # if lynx is available to do the dump.
47 fig2dev = whereis('fig2dev')
48 epydoc = whereis('epydoc')
49 groff = whereis('groff')
50 lynx = whereis('lynx')
51 man2html = whereis('man2html')
52 jade_original = whereis('jade')
53 jade = whereis('openjade') or jade_original
54 jadetex = whereis('jadetex')
55 pdfjadetex = whereis('pdfjadetex')
57 tidy = whereis('tidy')
62 entity_re = re.compile(r'<!entity\s+(?:%\s+)?(?:\S+)\s+SYSTEM\s+"([^"]*)">', re.I)
63 format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?')
66 # Find internal dependencies in .xml files:
68 # <!entity bground SYSTEM "bground.xml">
69 # <graphic fileref="file.jpg">
70 # <imagedata fileref="file.jpg">
72 # This only finds one per line, and assumes that anything
73 # defined as a SYSTEM entity is, in fact, a file included
74 # somewhere in the document.
76 def scanxml(node, env, target):
79 contents = node.get_text_contents()
81 includes.extend(entity_re.findall(contents))
83 matches = format_re.findall(contents)
86 if format and file[-len(format):] != format:
87 file = file + '.' + format
88 if not os.path.isabs(file):
92 f, tail = os.path.split(f)
96 file = os.path.join(*a)
101 s = Scanner(name = 'xml', function = scanxml, skeys = ['.xml', '.mod'])
104 env = orig_env.Clone(SCANNERS = [s],
105 SCONS_DOC_PY = File('#bin/scons-doc.py').rfile(),
106 SCONS_PROC_PY = File('#bin/scons-proc.py').rfile())
108 # Fetch the list of files in the build engine that contain
109 # SCons documentation XML for processing.
110 def chop(s): return s[:-1]
112 # If we ever read doc from __scons_doc__ strings in *.py files again,
113 # here's how it's done:
114 #manifest_in = File('#src/engine/MANIFEST.in').rstr()
115 #manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr()
116 #scons_doc_files = map(chop, open(manifest_in).readlines() +\
117 # open(manifest_xml_in).readlines())
118 #scons_doc_files = map(lambda x: '#src/engine/'+x, scons_doc_files)
119 #manifest_in = File('#src/engine/MANIFEST.in').rstr()
121 manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr()
122 scons_doc_files = list(map(chop, open(manifest_xml_in).readlines()))
123 scons_doc_files = [File('#src/engine/'+x).rstr() for x in scons_doc_files]
126 print "jw not found, skipping building User Guide."
129 # Always create a version.xml file containing the version information
130 # for this run. Ignore it for dependency purposes so we don't
131 # rebuild all the docs every time just because the date changes.
133 date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION')
134 version_xml = File(os.path.join(build, "version.xml"))
135 #version_xml = File("version.xml")
136 verfile = str(version_xml)
140 pass # okay if the file didn't exist
141 dir, f = os.path.split(verfile)
145 pass # okay if the directory already exists
146 open(verfile, "w").write("""<!--
147 THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
149 <!ENTITY builddate "%s">
150 <!ENTITY buildversion "%s">
151 <!ENTITY buildrevision "%s">
152 """ % (date, ver, rev))
154 builders_gen = os.path.join(build, 'user', 'builders.gen')
155 builders_mod = os.path.join(build, 'user', 'builders.mod')
156 tools_gen = os.path.join(build, 'user', 'tools.gen')
157 tools_mod = os.path.join(build, 'user', 'tools.mod')
158 variables_gen = os.path.join(build, 'user', 'variables.gen')
159 variables_mod = os.path.join(build, 'user', 'variables.mod')
161 # We put $( - $) around $SOURCES in the command line below because
162 # the path names will change when a given input file is found in
163 # a repository one run and locally the next, and we don't want
164 # to rebuild documentation just because it's found in one location
165 # vs. the other. The *.gen and *.mod targets will still be dependent
166 # on the list of the files themselves.
167 doc_output_files = [builders_gen, builders_mod,
168 tools_gen, tools_mod,
169 variables_gen, variables_mod]
170 b = env.Command(doc_output_files,
172 "$PYTHON $SCONS_PROC_PY --xml -b ${TARGETS[0]},${TARGETS[1]} -t ${TARGETS[2]},${TARGETS[3]} -v ${TARGETS[4]},${TARGETS[5]} $( $SOURCES $)")
173 env.Depends(b, "$SCONS_PROC_PY")
178 # Each document will live in its own subdirectory. List them here
179 # as hash keys, with a hash of the info to control its build.
183 'htmlindex' : 'book1.html',
188 # This doesn't build on all systems, and the document is old
189 # enough that there's reallyno need to build it every time any
190 # more, so just comment it out for now.
192 # 'htmlindex' : 't1.html',
207 'htmlindex' : 'book1.html',
213 # For whenever (if ever?) we start putting developer guide
214 # information in a printable document instead of the wiki.
216 # 'htmlindex' : 'book1.html',
223 'htmlindex' : 'book1.html',
229 'SCons-win32-install-1.jpg',
230 'SCons-win32-install-2.jpg',
231 'SCons-win32-install-3.jpg',
232 'SCons-win32-install-4.jpg',
239 # We have to tell SCons to scan the top-level XML files which
240 # get included by the document XML files in the subdirectories.
242 manifest = File('MANIFEST').rstr()
243 src_files = [x[:-1] for x in open(manifest).readlines()]
245 base, ext = os.path.splitext(s)
246 if ext in ['.fig', '.jpg']:
247 orig_env.Install(build, s)
249 orig_env.SCons_revision(os.path.join(build, s), s)
250 Local(os.path.join(build, s))
253 # For each document, build the document itself in HTML, Postscript,
256 for doc in docs.keys():
257 manifest = File(os.path.join(doc, 'MANIFEST')).rstr()
258 src_files = [x[:-1] for x in open(manifest).readlines()]
259 build_doc = docs[doc].get('scons-doc') and int(ARGUMENTS.get('BUILDDOC', 0))
261 doc_s = os.path.join(doc, s)
262 build_s = os.path.join(build, doc, s)
263 base, ext = os.path.splitext(doc_s)
264 if ext in ['.fig', '.jpg']:
265 orig_env.InstallAs(build_s, doc_s)
267 if build_doc and ext == '.xml':
270 "$PYTHON $SCONS_DOC_PY $SOURCE > $TARGET")
271 orig_env.SCons_revision(build_s, doc_s)
274 main = os.path.join(build, doc, 'main.xml')
277 # Hard-coding the scons-src path is a bit of a hack. This can
278 # be reworked when a better solution presents itself.
279 scons_src_main = os.path.join(build_dir, 'scons-src', 'doc', main)
280 env.Ignore(scons_src_main, version_xml)
282 htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc)
283 htmlindex = os.path.join(htmldir, docs[doc]['htmlindex'])
284 html = os.path.join(build, 'HTML', 'scons-%s.html' % doc)
285 ps = os.path.join(build, 'PS', 'scons-%s.ps' % doc)
286 pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc)
287 text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc)
289 if docs[doc].get('html') and jade:
290 def copy_index_html(target, source, env):
291 # Older versions of DocBook|jw|jade|whatever would
292 # create a book1.html file, while newer versions create
293 # an index.html file (logically enough). The scons.org
294 # web site links expect book1.html, so we're going to
295 # leave the target as is, and run this post-processing
296 # action function to check that the target really did
297 # get created, and if it didn't, copy it from index.html.
299 if not os.path.exists(t):
300 i = os.path.join(os.path.split(t)[0], 'index.html')
301 open(t, 'w').write(open(i, 'r').read())
305 Delete("${TARGET.dir}/*.html"),
306 "jw -b html -o ${TARGET.dir} $SOURCES",
309 cmds.append("tidy -m -q $TARGET || true")
310 cmds.append(Action(copy_index_html))
311 env.Command(htmlindex, File(main), cmds)
315 Delete("${TARGET.dir}/main.html"),
316 "jw -u -b html -o ${TARGET.dir} $SOURCES",
317 Move("$TARGET", "${TARGET.dir}/main.html"),
320 cmds.append("tidy -m -q $TARGET || true")
321 env.Command(html, File(main), cmds)
324 env.Ignore([html, htmlindex], version_xml)
326 tar_deps.extend([html, htmlindex])
327 tar_list.extend([html, htmldir])
329 for g in docs[doc].get('graphics', []):
330 base, ext = os.path.splitext(g)
333 htmldir_jpg = os.path.join(htmldir, jpg)
335 fig = os.path.join(build, doc, g)
336 env.Command(htmldir_jpg, fig,
337 "%s -L jpeg -q 100 $SOURCES $TARGET" % fig2dev)
339 env.InstallAs(htmldir_jpg, jpg)
340 env.Depends(html, htmldir_jpg)
343 src = os.path.join(build, doc, g)
344 Local(env.Install(htmldir, src))
346 if docs[doc].get('ps') and jadetex and jade_original:
347 env.Command(ps, main, [
348 Delete("${TARGET.dir}/%s" % out),
349 "jw -b ps -o ${TARGET.dir} -p %s $SOURCES" % jade_original,
350 "mv ${TARGET.dir}/main.ps $TARGET",
351 Delete("${TARGET.dir}/%s" % out),
355 env.Ignore(ps, version_xml)
360 for g in docs[doc].get('graphics', []):
361 base, ext = os.path.splitext(g)
364 build_eps = os.path.join(build, 'PS', eps)
366 fig = os.path.join(build, doc, g)
367 env.Command(build_eps, fig, "%s -L eps $SOURCES $TARGET" % fig2dev)
369 env.InstallAs(build_eps, eps)
370 env.Depends(ps, build_eps)
373 src = os.path.join(build, doc, g)
374 Local(env.Install(htmldir, src))
376 if docs[doc].get('pdf') and pdfjadetex and jade_original:
377 env.Command(pdf, main, [
378 Delete("${TARGET.dir}/%s" % out),
379 "jw -b pdf -o ${TARGET.dir} -p %s $SOURCES" % jade_original,
380 "mv ${TARGET.dir}/main.pdf $TARGET",
381 Delete("${TARGET.dir}/out"),
385 env.Ignore(pdf, version_xml)
390 if docs[doc].get('text') and jade and lynx:
391 env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET")
394 env.Ignore(text, version_xml)
396 tar_deps.append(text)
397 tar_list.append(text)
400 # Man page(s), in good ol' troff format.
402 man_page_list = ['scons.1', 'sconsign.1', 'scons-time.1']
404 for m in man_page_list:
405 x = orig_env.SCons_revision(os.path.join(build, 'man', m),
406 os.path.join('man', m))
408 man_i_files = ['builders.man', 'tools.man', 'variables.man']
410 man_intermediate_files = [os.path.join(build, 'man', x) for x in man_i_files]
412 cmd = "$PYTHON $SCONS_PROC_PY --man -b ${TARGETS[0]} -t ${TARGETS[1]} -v ${TARGETS[2]} $( $SOURCES $)"
413 man_intermediate_files = env.Command(man_intermediate_files,
416 env.Depends(man_intermediate_files, "$SCONS_PROC_PY")
417 Local(man_intermediate_files)
419 for man_1 in man_page_list:
420 man, _1 = os.path.splitext(man_1)
422 man_1 = os.path.join(build, 'man', man_1)
425 ps = os.path.join(build, 'PS', '%s-man.ps' % man)
426 text = os.path.join(build, 'TEXT', '%s-man.txt' % man)
428 b = env.Command(ps, man_1, "( cd ${SOURCES.dir} && groff -man -Tps ${SOURCES.file} ) > $TARGET")
430 env.Depends(b, man_intermediate_files)
432 b = env.Command(text, man_1, "( cd ${SOURCES.dir} && groff -man -Tascii ${SOURCES.file} ) > $TARGET")
434 env.Depends(b, man_intermediate_files)
436 tar_deps.extend([ps, text])
437 tar_list.extend([ps, text])
440 html = os.path.join(build, 'HTML' , '%s-man.html' % man)
442 def strip_to_first_html_tag(target, source, env):
444 contents = open(t).read()
445 contents = contents[contents.find('<HTML>'):]
446 open(t, 'w').write(contents)
450 "( cd %s/man && cp %s .. )" % (build, ' '.join(man_i_files)),
451 "( cd ${SOURCE.dir} && man2html ${SOURCE.file} ) > $TARGET",
452 Action(strip_to_first_html_tag),
455 cmds.append("tidy -m -q $TARGET || true")
456 b = env.Command(html, man_1, cmds)
458 env.Depends(b, man_intermediate_files)
460 tar_deps.append(html)
461 tar_list.append(html)
464 print "epydoc not found, skipping building API documentation."
466 # XXX Should be in common with reading the same thing in
467 # the SConstruct file.
468 e = os.path.join('#src', 'engine')
469 manifest_in = File(os.path.join(e, 'MANIFEST.in')).rstr()
470 sources = [x[:-1] for x in open(manifest_in).readlines()]
471 sources = [x for x in sources if x.find('Optik') == -1]
472 sources = [x for x in sources if x.find('Platform') == -1]
473 sources = [x for x in sources if x.find('Tool') == -1]
475 sources = [x for x in sources if x.find('Options') == -1]
477 e = os.path.join(build, '..', 'scons', 'engine')
478 sources = [os.path.join(e, x) for x in sources]
482 '$EPYDOC $EPYDOCFLAGS --debug --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES',
486 htmldir = os.path.join(build, 'HTML', 'scons-api')
487 env.Command('${OUTDIR}/index.html', sources, epydoc_commands,
488 EPYDOC=epydoc, EPYDOCFLAGS='--html', OUTDIR=htmldir)
489 tar_deps.append(htmldir)
490 tar_list.append(htmldir)
492 # PDF and PostScript and TeX are built from the
494 api_dir = os.path.join(build, 'scons-api')
495 api_pdf = os.path.join(api_dir, 'api.pdf')
496 api_ps = os.path.join(api_dir, 'api.ps')
497 api_tex = os.path.join(api_dir, 'api.tex')
498 api_targets = [api_pdf, api_ps, api_tex]
499 env.Command(api_targets, sources, epydoc_commands,
500 EPYDOC=epydoc, EPYDOCFLAGS='--pdf', OUTDIR=api_dir)
503 pdf_install = os.path.join(build, 'PDF', 'scons-api.pdf')
504 env.InstallAs(pdf_install, api_pdf)
505 tar_deps.append(pdf_install)
506 tar_list.append(pdf_install)
509 ps_install = os.path.join(build, 'PS', 'scons-api.ps')
510 env.InstallAs(ps_install, api_ps)
511 tar_deps.append(ps_install)
512 tar_list.append(ps_install)
516 # Now actually create the tar file of the documentation,
517 # for easy distribution to the web site.
520 tar_list = ' '.join([x.replace(build+'/', '') for x in tar_list])
521 t = env.Command(dist_doc_tar_gz, tar_deps,
522 "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list))
523 AddPostAction(dist_doc_tar_gz, Chmod(dist_doc_tar_gz, 0644))
527 Alias('doc', os.path.join(build_dir, 'doc'))