# # SConscript file for building SCons documentation. # # # __COPYRIGHT__ # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import os.path import re Import('build_dir', 'env', 'whereis') env = env.Clone() build = os.path.join(build_dir, 'doc') # # # dist_doc_tar_gz = '$DISTDIR/scons-doc-${VERSION}.tar.gz' # # We'll only try to build text files (for some documents) # if lynx is available to do the dump. # fig2dev = whereis('fig2dev') epydoc_cli = whereis('epydoc') groff = whereis('groff') lynx = whereis('lynx') man2html = whereis('man2html') jade_original = whereis('jade') jade = whereis('openjade') or jade_original jadetex = whereis('jadetex') pdfjadetex = whereis('pdfjadetex') jw = whereis('jw') tidy = whereis('tidy') tar_deps = [] tar_list = [] entity_re = re.compile(r'', re.I) format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?') # # Find internal dependencies in .xml files: # # # # # # This only finds one per line, and assumes that anything # defined as a SYSTEM entity is, in fact, a file included # somewhere in the document. # def scanxml(node, env, target): includes = [] contents = node.get_text_contents() includes.extend(entity_re.findall(contents)) matches = format_re.findall(contents) for m in matches: file, format = m if format and file[-len(format):] != format: file = file + '.' + format if not os.path.isabs(file): a = [] f = file while f: f, tail = os.path.split(f) if tail == 'doc': break a = [tail] + a file = os.path.join(*a) includes.append(file) return includes s = Scanner(name = 'xml', function = scanxml, skeys = ['.xml', '.mod']) orig_env = env env = orig_env.Clone(SCANNERS = [s], SCONS_DOC_PY = File('#bin/scons-doc.py').rfile(), SCONS_PROC_PY = File('#bin/scons-proc.py').rfile()) # Fetch the list of files in the build engine that contain # SCons documentation XML for processing. def chop(s): return s[:-1] # If we ever read doc from __scons_doc__ strings in *.py files again, # here's how it's done: #manifest_in = File('#src/engine/MANIFEST.in').rstr() #manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr() #scons_doc_files = map(chop, open(manifest_in).readlines() +\ # open(manifest_xml_in).readlines()) #scons_doc_files = map(lambda x: '#src/engine/'+x, scons_doc_files) #manifest_in = File('#src/engine/MANIFEST.in').rstr() manifest_xml_in = File('#src/engine/MANIFEST-xml.in').rstr() scons_doc_files = list(map(chop, open(manifest_xml_in).readlines())) scons_doc_files = [File('#src/engine/'+x).rstr() for x in scons_doc_files] if not jw: print "doc: jw not found, skipping building User Guide." else: # # Always create a version.xml file containing the version information # for this run. Ignore it for dependency purposes so we don't # rebuild all the docs every time just because the date changes. # date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION') version_xml = File(os.path.join(build, "version.xml")) #version_xml = File("version.xml") verfile = str(version_xml) try: os.unlink(verfile) except OSError: pass # okay if the file didn't exist dir, f = os.path.split(verfile) try: os.makedirs(dir) except OSError: pass # okay if the directory already exists open(verfile, "w").write(""" """ % (date, ver, rev)) builders_gen = os.path.join(build, 'user', 'builders.gen') builders_mod = os.path.join(build, 'user', 'builders.mod') functions_gen = os.path.join(build, 'user', 'functions.gen') functions_mod = os.path.join(build, 'user', 'functions.mod') tools_gen = os.path.join(build, 'user', 'tools.gen') tools_mod = os.path.join(build, 'user', 'tools.mod') variables_gen = os.path.join(build, 'user', 'variables.gen') variables_mod = os.path.join(build, 'user', 'variables.mod') # We put $( - $) around $SOURCES in the command line below because # the path names will change when a given input file is found in # a repository one run and locally the next, and we don't want # to rebuild documentation just because it's found in one location # vs. the other. The *.gen and *.mod targets will still be dependent # on the list of the files themselves. doc_output_files = [builders_gen, builders_mod, functions_gen, functions_mod, tools_gen, tools_mod, variables_gen, variables_mod] b = env.Command(doc_output_files, scons_doc_files, "$PYTHON $SCONS_PROC_PY --xml -b ${TARGETS[0]},${TARGETS[1]} -f ${TARGETS[2]},${TARGETS[3]} -t ${TARGETS[4]},${TARGETS[5]} -v ${TARGETS[6]},${TARGETS[7]} $( $SOURCES $)") env.Depends(b, "$SCONS_PROC_PY") env.Local(b) # # Each document will live in its own subdirectory. List them here # as hash keys, with a hash of the info to control its build. # docs = { 'design' : { 'htmlindex' : 'book1.html', 'ps' : 1, 'pdf' : 1, 'text' : 0, }, # This doesn't build on all systems, and the document is old # enough that there's reallyno need to build it every time any # more, so just comment it out for now. #'python10' : { # 'htmlindex' : 't1.html', # 'html' : 1, # 'ps' : 1, # 'pdf' : 0, # 'text' : 0, # 'graphics' : [ # 'arch.fig', # 'builder.fig', # 'job-task.fig', # 'node.fig', # 'scanner.fig', # 'sig.fig' # ], #}, 'reference' : { 'htmlindex' : 'book1.html', 'html' : 1, 'ps' : 1, 'pdf' : 1, 'text' : 0, }, # For whenever (if ever?) we start putting developer guide # information in a printable document instead of the wiki. #'developer' : { # 'htmlindex' : 'book1.html', # 'html' : 1, # 'ps' : 1, # 'pdf' : 1, # 'text' : 0, #}, 'user' : { 'htmlindex' : 'book1.html', 'html' : 1, 'ps' : 1, 'pdf' : 1, 'text' : 1, 'graphics' : [ 'SCons-win32-install-1.jpg', 'SCons-win32-install-2.jpg', 'SCons-win32-install-3.jpg', 'SCons-win32-install-4.jpg', ], 'scons-doc' : 1, }, } # # We have to tell SCons to scan the top-level XML files which # get included by the document XML files in the subdirectories. # manifest = File('MANIFEST').rstr() src_files = [x[:-1] for x in open(manifest).readlines()] for s in src_files: base, ext = os.path.splitext(s) if ext in ['.fig', '.jpg']: orig_env.Install(build, s) else: orig_env.SCons_revision(os.path.join(build, s), s) Local(os.path.join(build, s)) # # For each document, build the document itself in HTML, Postscript, # and PDF formats. # for doc in docs.keys(): manifest = File(os.path.join(doc, 'MANIFEST')).rstr() src_files = [x[:-1] for x in open(manifest).readlines()] build_doc = docs[doc].get('scons-doc') and int(ARGUMENTS.get('BUILDDOC', 0)) for s in src_files: doc_s = os.path.join(doc, s) build_s = os.path.join(build, doc, s) base, ext = os.path.splitext(doc_s) if ext in ['.fig', '.jpg']: orig_env.InstallAs(build_s, doc_s) else: if build_doc and ext == '.xml': env.Command(doc_s, base + '.in', "$PYTHON $SCONS_DOC_PY $SOURCE > $TARGET") orig_env.SCons_revision(build_s, doc_s) Local(build_s) main = os.path.join(build, doc, 'main.xml') out = 'main.out' # Hard-coding the scons-src path is a bit of a hack. This can # be reworked when a better solution presents itself. scons_src_main = os.path.join(build_dir, 'scons-src', 'doc', main) env.Ignore(scons_src_main, version_xml) htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc) htmlindex = os.path.join(htmldir, docs[doc]['htmlindex']) html = os.path.join(build, 'HTML', 'scons-%s.html' % doc) ps = os.path.join(build, 'PS', 'scons-%s.ps' % doc) pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc) text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc) if docs[doc].get('html') and jade: def copy_index_html(target, source, env): # Older versions of DocBook|jw|jade|whatever would # create a book1.html file, while newer versions create # an index.html file (logically enough). The scons.org # web site links expect book1.html, so we're going to # leave the target as is, and run this post-processing # action function to check that the target really did # get created, and if it didn't, copy it from index.html. t = str(target[0]) if not os.path.exists(t): i = os.path.join(os.path.split(t)[0], 'index.html') open(t, 'w').write(open(i, 'r').read()) return None cmds = [ Delete("${TARGET.dir}/*.html"), "jw -b html -o ${TARGET.dir} $SOURCES", ] if tidy: cmds.append("tidy -m -q $TARGET || true") cmds.append(Action(copy_index_html)) env.Command(htmlindex, File(main), cmds) Local(htmlindex) cmds = [ Delete("${TARGET.dir}/main.html"), "jw -u -b html -o ${TARGET.dir} $SOURCES", Move("$TARGET", "${TARGET.dir}/main.html"), ] if tidy: cmds.append("tidy -m -q $TARGET || true") env.Command(html, File(main), cmds) Local(html) env.Ignore([html, htmlindex], version_xml) tar_deps.extend([html, htmlindex]) tar_list.extend([html, htmldir]) for g in docs[doc].get('graphics', []): base, ext = os.path.splitext(g) if ext == '.fig': jpg = base + '.jpg' htmldir_jpg = os.path.join(htmldir, jpg) if fig2dev: fig = os.path.join(build, doc, g) env.Command(htmldir_jpg, fig, "%s -L jpeg -q 100 $SOURCES $TARGET" % fig2dev) else: env.InstallAs(htmldir_jpg, jpg) env.Depends(html, htmldir_jpg) Local(htmldir_jpg) else: src = os.path.join(build, doc, g) Local(env.Install(htmldir, src)) if docs[doc].get('ps') and jadetex and jade_original: env.Command(ps, main, [ Delete("${TARGET.dir}/%s" % out), "jw -b ps -o ${TARGET.dir} -p %s $SOURCES" % jade_original, "mv ${TARGET.dir}/main.ps $TARGET", Delete("${TARGET.dir}/%s" % out), ]) Local(ps) env.Ignore(ps, version_xml) tar_deps.append(ps) tar_list.append(ps) for g in docs[doc].get('graphics', []): base, ext = os.path.splitext(g) if ext == '.fig': eps = base + '.eps' build_eps = os.path.join(build, 'PS', eps) if fig2dev: fig = os.path.join(build, doc, g) env.Command(build_eps, fig, "%s -L eps $SOURCES $TARGET" % fig2dev) else: env.InstallAs(build_eps, eps) env.Depends(ps, build_eps) Local(build_eps) else: src = os.path.join(build, doc, g) Local(env.Install(htmldir, src)) if docs[doc].get('pdf') and pdfjadetex and jade_original: env.Command(pdf, main, [ Delete("${TARGET.dir}/%s" % out), "jw -b pdf -o ${TARGET.dir} -p %s $SOURCES" % jade_original, "mv ${TARGET.dir}/main.pdf $TARGET", Delete("${TARGET.dir}/out"), ]) Local(pdf) env.Ignore(pdf, version_xml) tar_deps.append(pdf) tar_list.append(pdf) if docs[doc].get('text') and jade and lynx: env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET") Local(text) env.Ignore(text, version_xml) tar_deps.append(text) tar_list.append(text) # # Man page(s), in good ol' troff format. # man_page_list = ['scons.1', 'sconsign.1', 'scons-time.1'] for m in man_page_list: x = orig_env.SCons_revision(os.path.join(build, 'man', m), os.path.join('man', m)) man_i_files = ['builders.man', 'functions.man', 'tools.man', 'variables.man'] man_intermediate_files = [os.path.join(build, 'man', x) for x in man_i_files] cmd = "$PYTHON $SCONS_PROC_PY --man -b ${TARGETS[0]} -f ${TARGETS[1]} -t ${TARGETS[2]} -v ${TARGETS[3]} $( $SOURCES $)" man_intermediate_files = env.Command(man_intermediate_files, scons_doc_files, cmd) env.Depends(man_intermediate_files, "$SCONS_PROC_PY") Local(man_intermediate_files) for man_1 in man_page_list: man, _1 = os.path.splitext(man_1) man_1 = os.path.join(build, 'man', man_1) if groff: ps = os.path.join(build, 'PS', '%s-man.ps' % man) text = os.path.join(build, 'TEXT', '%s-man.txt' % man) b = env.Command(ps, man_1, "( cd ${SOURCES.dir} && groff -man -Tps ${SOURCES.file} ) > $TARGET") Local(ps) env.Depends(b, man_intermediate_files) b = env.Command(text, man_1, "( cd ${SOURCES.dir} && groff -man -Tascii ${SOURCES.file} ) > $TARGET") Local(text) env.Depends(b, man_intermediate_files) tar_deps.extend([ps, text]) tar_list.extend([ps, text]) if man2html: html = os.path.join(build, 'HTML' , '%s-man.html' % man) def strip_to_first_html_tag(target, source, env): t = str(target[0]) contents = open(t).read() contents = contents[contents.find(''):] open(t, 'w').write(contents) return 0 cmds = [ "( cd %s/man && cp %s .. )" % (build, ' '.join(man_i_files)), "( cd ${SOURCE.dir} && man2html ${SOURCE.file} ) > $TARGET", Action(strip_to_first_html_tag), ] if tidy: cmds.append("tidy -m -q $TARGET || true") b = env.Command(html, man_1, cmds) Local(html) env.Depends(b, man_intermediate_files) tar_deps.append(html) tar_list.append(html) if not epydoc_cli: try: import epydoc except ImportError: epydoc = None else: # adding Epydoc builder using imported module def epydoc_builder_action(target, source, env): """ Take a list of `source` files and build docs for them in `target` dir. `target` and `source` are lists. Uses OUTDIR and EPYDOCFLAGS environment variables. http://www.scons.org/doc/2.0.1/HTML/scons-user/x3594.html """ # the epydoc build process is the following: # 1. build documentation index # 2. feed doc index to writer for docs from epydoc.docbuilder import build_doc_index from epydoc.docwriter.html import HTMLWriter from epydoc.docwriter.latex import LatexWriter # first arg is a list where can be names of python package dirs, # python files, object names or objects itself docindex = build_doc_index([str(src) for src in source]) if docindex == None: return -1 if env['EPYDOCFLAGS'] == '--html': html_writer = HTMLWriter(docindex, docformat='restructuredText', prj_name='SCons', prj_url='http://www.scons.org/') try: html_writer.write(env['OUTDIR']) except OSError: # If directory cannot be created or any file cannot # be created or written to. return -2 """ # PDF support requires external Linux utilites, so it's not crossplatform. # Leaving for now. # http://epydoc.svn.sourceforge.net/viewvc/epydoc/trunk/epydoc/src/epydoc/cli.py elif env['EPYDOCFLAGS'] == '--pdf': pdf_writer = LatexWriter(docindex, docformat='restructuredText', prj_name='SCons', prj_url='http://www.scons.org/') """ return 0 epydoc_commands = [ Delete('$OUTDIR'), epydoc_builder_action, Touch('$TARGET'), ] else: # epydoc_cli is found epydoc_commands = [ Delete('$OUTDIR'), '$EPYDOC $EPYDOCFLAGS --debug --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES', Touch('$TARGET'), ] if not epydoc_cli and not epydoc: print "doc: epydoc not found, skipping building API documentation." else: # XXX Should be in common with reading the same thing in # the SConstruct file. e = os.path.join('#src', 'engine') manifest_in = File(os.path.join(e, 'MANIFEST.in')).rstr() sources = [x[:-1] for x in open(manifest_in).readlines()] sources = [x for x in sources if x.find('Optik') == -1] sources = [x for x in sources if x.find('Platform') == -1] sources = [x for x in sources if x.find('Tool') == -1] # XXX sources = [x for x in sources if x.find('Options') == -1] e = os.path.join(build, '..', 'scons', 'engine') sources = [os.path.join(e, x) for x in sources] htmldir = os.path.join(build, 'HTML', 'scons-api') env.Command('${OUTDIR}/index.html', sources, epydoc_commands, EPYDOC=epydoc_cli, EPYDOCFLAGS='--html', OUTDIR=htmldir) tar_deps.append(htmldir) tar_list.append(htmldir) if not epydoc_cli: print "doc: command line epydoc is not found, skipping PDF/PS/Tex output" else: # PDF and PostScript and TeX are built from the # same invocation. api_dir = os.path.join(build, 'scons-api') api_pdf = os.path.join(api_dir, 'api.pdf') api_ps = os.path.join(api_dir, 'api.ps') api_tex = os.path.join(api_dir, 'api.tex') api_targets = [api_pdf, api_ps, api_tex] env.Command(api_targets, sources, epydoc_commands, EPYDOC=epydoc_cli, EPYDOCFLAGS='--pdf', OUTDIR=api_dir) Local(api_targets) pdf_install = os.path.join(build, 'PDF', 'scons-api.pdf') env.InstallAs(pdf_install, api_pdf) tar_deps.append(pdf_install) tar_list.append(pdf_install) Local(pdf_install) ps_install = os.path.join(build, 'PS', 'scons-api.ps') env.InstallAs(ps_install, api_ps) tar_deps.append(ps_install) tar_list.append(ps_install) Local(ps_install) # # Now actually create the tar file of the documentation, # for easy distribution to the web site. # if tar_deps: tar_list = ' '.join([x.replace(build+'/', '') for x in tar_list]) t = env.Command(dist_doc_tar_gz, tar_deps, "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list)) AddPostAction(dist_doc_tar_gz, Chmod(dist_doc_tar_gz, 0644)) Local(t) Alias('doc', t) else: Alias('doc', os.path.join(build_dir, 'doc'))