source: rtems-docs/common/waf.py @ 5ce8e43

5
Last change on this file since 5ce8e43 was 5ce8e43, checked in by Chris Johns <chrisj@…>, on 08/21/18 at 03:41:51

build: Fix indexing so it works on HTML and PDF.

  • Remove all genindex.rst files, these are generated and should not exist in our source.
  • Fix the HTML templates so the local specialisation works.
  • Add a index link to the sidebar for HTML.

Note, there is no TOC entry for the index in the PDF output and I
cannot figure out how to add one.

Closes #3503

  • Property mode set to 100644
File size: 16.5 KB
RevLine 
[5ce8e43]1#
2# RTEMS Documentation Project
3#
4# Waf build support.
5#
6
7
8from __future__ import print_function
9
10import os
11import re
12import sys
13
[f916fca]14from waflib.Build import BuildContext
[5daabd2]15
[8330198]16import latex
17
[0bc9c6d]18sphinx_min_version = (1, 3)
[a316b1f]19
[f97be09]20def build_date():
21    import datetime
22    now = datetime.date.today()
23    m = now.strftime('%B')
24    y = now.strftime('%Y')
25    if now.day % 10 == 1:
26        s = 'st'
27    elif now.day % 10 == 2:
28        s = 'nd'
29    elif now.day == 3:
30        s = 'rd'
31    else:
32        s = 'th'
33    d = '%2d%s' % (now.day, s)
34    return '%s %s %s' % (d, m, y)
35
[0263581]36def version_cmdline(ctx):
[33989b6]37    return '-Drelease="%s" -Dversion="%s"' % (ctx.env.VERSION, ctx.env.VERSION)
[0263581]38
[5ce8e43]39def sphinx_cmdline(ctx, build_type, conf_dir, doctrees,
40                   source_dir, output_dir, configs = []):
41    cfgs = ''
42    for c in configs:
43        cfgs += ' -D %s=%s' % (c, configs[c])
44    rule = "${BIN_SPHINX_BUILD} %s -b %s -c %s %s -d %s %s %s %s ${SRC}" % \
[0263581]45           (sphinx_verbose(ctx), build_type, conf_dir, version_cmdline(ctx),
[5ce8e43]46            doctrees, cfgs, source_dir, output_dir)
[0263581]47    return rule
48
[f916fca]49def cmd_spell(ctx):
[0bc9c6d]50    from waflib import Options
51    from sys import argv
52    from subprocess import call
[f916fca]53
[0bc9c6d]54    Options.commands = None # stop warnings about knowing commands.
[f916fca]55
[0bc9c6d]56    if not ctx.env.BIN_ASPELL:
57        ctx.fatal("'aspell' is required please install and re-run configure.")
[f916fca]58
[0bc9c6d]59    if len(argv) < 3:
60        ctx.fatal("Please supply at least one file name")
[f916fca]61
[0bc9c6d]62    files = argv[2:]
[f916fca]63
[0bc9c6d]64    path = ctx.path.parent.abspath()
[f916fca]65
[0bc9c6d]66    # XXX: add error checking eg check if file exists.
67    for file in files:
68        cmd = ctx.env.BIN_ASPELL + \
69              ["-c",
70               "--personal=%s/common/spell/dict/rtems" % path,
71               "--extra-dicts=%s/common/spell/en_GB-ise-w_accents.multi" % path,
72               file]
73        print("running:", cmd)
74        call(cmd)
[f916fca]75
[0263581]76def cmd_linkcheck(ctx):
77    conf_dir = ctx.path.get_src()
78    source_dir = ctx.path.get_src()
79    buildtype = 'linkcheck'
80    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
81    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
[0bc9c6d]82    ctx(
[0263581]83        rule   = rule,
[0bc9c6d]84        cwd    = ctx.path.abspath(),
85        source = ctx.path.ant_glob('**/*.rst'),
86        target = "linkcheck/output.txt"
87    )
[ed3794e]88
[f916fca]89class spell(BuildContext):
[0bc9c6d]90    __doc__ = "Check spelling.  Supply a list of files or a glob (*.rst)"
91    cmd = 'spell'
92    fun = 'cmd_spell'
[3a71759]93
[ed3794e]94class linkcheck(BuildContext):
[0bc9c6d]95    __doc__ = "Check all external URL references."
96    cmd = 'linkcheck'
97    fun = 'cmd_linkcheck'
[ed3794e]98
[14bbcb1]99def check_sphinx_version(ctx, minver):
[0bc9c6d]100    try:
[2a06644]101        import sphinx
102        # sphinx.version_info was introduced in sphinx ver 1.2
103        version = sphinx.version_info
104        # version looks like (1, 7, 0, 'final', 0))
105        ver = version[0:2]
[0bc9c6d]106    except:
[2a06644]107        try:
108            # sphinx-build returns its version info in stderr
109            (out, err) = ctx.cmd_and_log(ctx.env.BIN_SPHINX_BUILD +
110                              ['--version'], output=Context.BOTH)
111            # err looks like 'sphinx-build 1.7.0\n'
112            version = err.split(" ")[-1:][0].strip()
113            ver = tuple(map(int, re.split('[\D]', version)))
114        except:
115            try:
116                # sphinx-build returns its version info in stdout
117                version = ctx.cmd_and_log(ctx.env.BIN_SPHINX_BUILD +
118                              ['--version']).split(" ")[-1:][0].strip()
119                try:
120                    ver = tuple(map(int, re.split('[\D]', version)))
121                except:
122                    ctx.fatal("Sphinx version cannot be checked")
123            except:
124                ctx.fatal("Sphinx version cannot be checked: %s" % version)
[0bc9c6d]125    if ver < minver:
126        ctx.fatal("Sphinx version is too old: %s" % ".".join(map(str, ver)))
127    return ver
[14bbcb1]128
[f529417]129def sphinx_verbose(ctx):
[0bc9c6d]130    return ' '.join(ctx.env.SPHINX_VERBOSE)
131
132def is_top_build(ctx):
133    from_top = False
134    if ctx.env['BUILD_FROM_TOP'] and ctx.env['BUILD_FROM_TOP'] == 'yes':
135        from_top = True
136    return from_top
137
138def build_dir_setup(ctx, buildtype):
139    where = buildtype
140    if is_top_build(ctx):
141        where = os.path.join(ctx.path.name, where)
[9024cfb]142    bnode = ctx.bldnode.find_node(where)
[0bc9c6d]143    if bnode is None:
144        ctx.bldnode.make_node(where).mkdir()
145    build_dir = ctx.path.get_bld().relpath()
146    output_node = ctx.path.get_bld().make_node(buildtype)
147    output_dir = output_node.abspath()
[6207c37]148    doctrees = os.path.join(os.path.dirname(output_dir), 'doctrees', buildtype)
149    return build_dir, output_node, output_dir, doctrees
[0bc9c6d]150
[8330198]151def pdf_resources(ctx, buildtype):
[2fdbc98]152    packages_base = ctx.path.parent.find_dir('common/latex')
153    if packages_base is None:
154        ctx.fatal('Latex package directory not found')
155    base = packages_base.path_from(ctx.path)
156    fnode = ctx.path.get_bld().make_node(buildtype)
157    fnode.mkdir()
[8330198]158    local_packages = latex.local_packages()
[5ce8e43]159    targets = []
[8330198]160    if local_packages is not None:
161        srcs = [os.path.join(base, p) for p in local_packages]
[5ce8e43]162        targets += [fnode.make_node(p) for p in local_packages]
163        ctx(features = "subst",
[8330198]164            is_copy  = True,
165            source   = srcs,
[5ce8e43]166            target   = targets)
167    targets += [fnode.make_node('rtemsextrafonts.sty')]
168    ctx(features = "subst",
[2fdbc98]169        is_copy  = True,
170        source   = os.path.join(base, ctx.env.RTEMSEXTRAFONTS),
[5ce8e43]171        target   = fnode.make_node('rtemsextrafonts.sty'))
172    return targets
[8330198]173
[0bc9c6d]174def html_resources(ctx, buildtype):
[5ce8e43]175    extra_source = []
[0bc9c6d]176    for dir_name in ["_static", "_templates"]:
177        files = ctx.path.parent.find_node("common").ant_glob("%s/*" % dir_name)
178        fnode = ctx.path.get_bld().make_node(os.path.join(buildtype, dir_name))
[5ce8e43]179        targets = [fnode.make_node(x.name) for x in files]
180        extra_source += targets
[0bc9c6d]181        fnode.mkdir() # dirs
[5ce8e43]182        ctx(features = "subst",
[0bc9c6d]183            is_copy  = True,
184            source   = files,
[5ce8e43]185            target   = targets)
186        ctx.add_group()
187    return extra_source
[74194f7]188
[ff9d555]189def check_sphinx_extension(ctx, extension):
190    def run_sphinx(bld):
191        rst_node = bld.srcnode.make_node('testbuild/contents.rst')
192        rst_node.parent.mkdir()
193        rst_node.write('.. COMMENT test sphinx\n')
194        bld(rule = bld.kw['rule'], source = rst_node)
195
196    ctx.start_msg("Checking for '%s'" % (extension))
197    try:
198        ctx.run_build(fragment = 'xx',
199                      rule = "${BIN_SPHINX_BUILD} -b html -D extensions=%s -C . out" % (extension),
200                      build_fun = run_sphinx,
201                      env = ctx.env)
202    except ctx.errors.ConfigurationError:
203        ctx.end_msg('not found (see README.txt)', 'RED')
204        ctx.fatal('The configuration failed')
205    ctx.end_msg('found')
[0bc9c6d]206
[ff9d555]207def cmd_configure(ctx):
208    check_sphinx = not ctx.env.BIN_SPHINX_BUILD
209    if check_sphinx:
[f97be09]210        ctx.msg('Checking version', ctx.env.VERSION)
211
[ff9d555]212        ctx.find_program("sphinx-build", var="BIN_SPHINX_BUILD", mandatory = True)
213        ctx.find_program("aspell", var = "BIN_ASPELL", mandatory = False)
214
215        ctx.start_msg("Checking if Sphinx is at least %s.%s" % sphinx_min_version)
216        ver = check_sphinx_version(ctx, sphinx_min_version)
217        ctx.end_msg("yes (%s)" % ".".join(map(str, ver)))
218
219        ctx.start_msg("Checking Sphinx Verbose ")
220        if 'SPHINX_VERBOSE' not in ctx.env:
221            ctx.env.append_value('SPHINX_VERBOSE', ctx.options.sphinx_verbose)
222            level = sphinx_verbose(ctx)
223            if level == '-Q':
224                level = 'quiet'
225            ctx.end_msg(level)
226        #
227        # Check extensions.
228        #
229        check_sphinx_extension(ctx, 'sphinx.ext.autodoc')
230        check_sphinx_extension(ctx, 'sphinx.ext.coverage')
231        check_sphinx_extension(ctx, 'sphinx.ext.doctest')
232        check_sphinx_extension(ctx, 'sphinx.ext.graphviz')
233        check_sphinx_extension(ctx, 'sphinx.ext.intersphinx')
234        check_sphinx_extension(ctx, 'sphinx.ext.mathjax')
[58de62d]235        check_sphinx_extension(ctx, 'sphinxcontrib.bibtex')
[0bc9c6d]236
237    #
238    # Optional builds.
239    #
240    ctx.env.BUILD_PDF = 'no'
241    if ctx.options.pdf:
[74194f7]242        check_tex = not ctx.env.PDFLATEX
243        if check_tex:
244            ctx.load('tex')
245            if not ctx.env.PDFLATEX or not ctx.env.MAKEINDEX:
246                ctx.fatal('The programs pdflatex and makeindex are required for PDF output')
247            if 'PDFLATEXFLAGS' not in ctx.env or \
248               '-shell-escape' not in ctx.env['PDFLATEXFLAGS']:
249                ctx.env.append_value('PDFLATEXFLAGS', '-shell-escape')
[8330198]250            latex.configure_tests(ctx)
[0bc9c6d]251        ctx.env.BUILD_PDF = 'yes'
252
253    ctx.envBUILD_SINGLEHTML = 'no'
254    if ctx.options.singlehtml:
[91d6c96]255        check_inliner = not ctx.env.BIN_INLINER
256        if check_inliner:
257            ctx.env.BUILD_SINGLEHTML = 'yes'
258            ctx.find_program("inliner", var = "BIN_INLINER", mandatory = False)
259            if not ctx.env.BIN_INLINER:
260                ctx.fatal("Node inliner is required install with 'npm install -g inliner' " +
261                          "(https://github.com/remy/inliner)")
[dd43d0b]262
[8e8094a]263def doc_pdf(ctx, source_dir, conf_dir, extra_source):
[0bc9c6d]264    buildtype = 'latex'
[6207c37]265    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
[5ce8e43]266    resources = pdf_resources(ctx, buildtype)
[228560f]267    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
[0bc9c6d]268    ctx(
269        rule         = rule,
270        cwd          = ctx.path,
[8e8094a]271        source       = ctx.path.ant_glob('**/*.rst') + extra_source,
[5ce8e43]272        depends_on   = extra_source,
[0bc9c6d]273        target       = ctx.path.find_or_declare("%s/%s.tex" % (buildtype,
274                                                               ctx.path.name))
275    )
276    ctx(
277        features     = 'tex',
278        cwd          = output_dir,
279        type         = 'pdflatex',
280        source       = "%s/%s.tex" % (buildtype, ctx.path.name),
281        prompt       = 0
282    )
283    ctx.install_files('${PREFIX}',
284                      '%s/%s.pdf' % (buildtype, ctx.path.name),
285                      cwd = output_node,
286                      quiet = True)
[dd43d0b]287
[8e8094a]288def doc_singlehtml(ctx, source_dir, conf_dir, extra_source):
[0bc9c6d]289    #
290    # Use a run command to handle stdout and stderr output from inliner. Using
291    # a standard rule in the build context locks up.
292    #
293    def run(task):
294        src = task.inputs[0].abspath()
295        tgt = task.outputs[0].abspath()
296        cmd = '%s %s' % (task.env.BIN_INLINER[0], src)
297        so = open(tgt, 'w')
298        se = open(tgt + '.err', 'w')
299        r = task.exec_command(cmd, stdout = so, stderr = se)
300        so.close()
301        se.close()
302        #
303        # The inliner does not handle internal href's correctly and places the
304        # input's file name in the href. Strip these.
305        #
306        with open(tgt, 'r') as i:
307            before = i.read()
308            after = before.replace('index.html', '')
309        i.close()
310        with open(tgt, 'w') as o:
311            o.write(after)
312        o.close()
313        return r
314
315    buildtype = 'singlehtml'
[6207c37]316    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
[5ce8e43]317    resource = html_resources(ctx, buildtype)
[228560f]318    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
[0bc9c6d]319    ctx(
320        rule         = rule,
321        cwd          = ctx.path,
[8e8094a]322        source       = ctx.path.ant_glob('**/*.rst') + extra_source,
[5ce8e43]323        depends_on   = resources,
[0bc9c6d]324        target       = ctx.path.find_or_declare("%s/index.html" % (buildtype)),
325        install_path = None
326    )
327    ctx(
328        rule         = run,
329        inliner      = ctx.env.BIN_INLINER,
330        source       = "%s/index.html" % buildtype,
331        target       = "%s/%s.html" % (buildtype, ctx.path.name),
332        install_path = '${PREFIX}'
333    )
[a316b1f]334
[8e8094a]335def doc_html(ctx, source_dir, conf_dir, extra_source):
[0bc9c6d]336    buildtype = 'html'
[6207c37]337    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
[5ce8e43]338    resources = html_resources(ctx, buildtype)
339    templates = os.path.join(str(ctx.path.get_bld()), buildtype, '_templates')
340    configs = { 'templates_path': templates }
341    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir, configs)
[0bc9c6d]342    ctx(
343        rule         = rule,
344        cwd          = ctx.path,
[8e8094a]345        source       = ctx.path.ant_glob('**/*.rst') + extra_source,
[5ce8e43]346        depends_on   = resources,
[0bc9c6d]347        target       = ctx.path.find_or_declare('%s/index.html' % buildtype),
348        install_path = None
349    )
350    ctx.install_files('${PREFIX}/%s' % (ctx.path.name),
351                      output_node.ant_glob('**/*', quiet = True),
352                      cwd = output_node,
353                      relative_trick = True,
354                      quiet = True)
[a316b1f]355
[8e8094a]356def cmd_build(ctx, extra_source = []):
[0263581]357    conf_dir = ctx.path.get_src()
358    source_dir = ctx.path.get_src()
[170418a]359
[0bc9c6d]360    if ctx.env.BUILD_PDF == 'yes':
[8e8094a]361        doc_pdf(ctx, source_dir, conf_dir, extra_source)
[a316b1f]362
[0bc9c6d]363    if ctx.env.BUILD_SINGLEHTML == 'yes':
[8e8094a]364        doc_singlehtml(ctx, source_dir, conf_dir, extra_source)
[9b5801a]365
[8e8094a]366    doc_html(ctx, source_dir, conf_dir, extra_source)
[a316b1f]367
[0bc9c6d]368def cmd_options(ctx):
[9330bfb]369    ctx.add_option('--disable-extra-fonts',
370                   action = 'store_true',
371                   default = False,
372                   help = "Disable building with extra fonts for better quality (lower quality).")
[0bc9c6d]373    ctx.add_option('--sphinx-verbose',
374                   action = 'store',
375                   default = "-Q",
376                   help = "Sphinx verbose.")
377    ctx.add_option('--pdf',
[228560f]378                   action = 'store_true',
[0bc9c6d]379                   default = False,
380                   help = "Build PDF.")
381    ctx.add_option('--singlehtml',
[228560f]382                   action = 'store_true',
[0bc9c6d]383                   default = False,
384                   help = "Build Single HTML file, requires Node Inliner")
[5daabd2]385
[3a71759]386def cmd_options_path(ctx):
[0bc9c6d]387    cmd_options(ctx)
388    ctx.add_option('--rtems-path-py',
389                   type = 'string',
390                   help = "Full path to py/ in RTEMS source repository.")
[3a71759]391
392def cmd_configure_path(ctx):
[0bc9c6d]393    if not ctx.options.rtems_path_py:
394        ctx.fatal("--rtems-path-py is required")
[3a71759]395
[0bc9c6d]396    ctx.env.RTEMS_PATH = ctx.options.rtems_path_py
[3a71759]397
[0bc9c6d]398    cmd_configure(ctx)
[3a71759]399
[f97be09]400def xml_catalogue(ctx, building):
[782b4fe]401    #
402    # The following is a hack to find the top_dir because the task does
403    # provided a reference to top_dir like a build context.
404    #
405    top_dir = ctx.get_cwd().find_node('..')
[1a9b02e]406    #
407    # Read the conf.py files in each directory to gather the doc details.
408    #
[782b4fe]409    catalogue = {}
[1a9b02e]410    sp = sys.path[:]
[782b4fe]411    for doc in building:
412        #
413        # Import using the imp API so the module is reloaded for us.
414        #
415        import imp
[feb6832]416        sys.path = [top_dir.find_node(doc).abspath()]
[782b4fe]417        mf = imp.find_module('conf')
[feb6832]418        sys.path = sp[:]
[782b4fe]419        try:
420            bconf = imp.load_module('bconf', mf[0], mf[1], mf[2])
421        finally:
422            mf[0].close()
423        catalogue[doc] = {
424            'title': bconf.project,
[f97be09]425            'version': str(ctx.env.VERSION),
426            'release': str(ctx.env.VERSION),
[782b4fe]427            'pdf': bconf.latex_documents[0][1].replace('.tex', '.pdf'),
428            'html': '%s/index.html' % (doc),
429            'singlehtml': '%s.html' % (doc)
430        }
431        bconf = None
[1a9b02e]432
433    import xml.dom.minidom as xml
434    cat = xml.Document()
435
436    root = cat.createElement('rtems-docs')
[f97be09]437    root.setAttribute('date', build_date())
[1a9b02e]438    cat.appendChild(root)
439
[9aa52b9]440    heading = cat.createElement('catalogue')
[f97be09]441    text = cat.createTextNode(str(ctx.env.VERSION))
[9aa52b9]442    heading.appendChild(text)
443    root.appendChild(heading)
444
[782b4fe]445    builds = ['html']
446    if ctx.env.BUILD_PDF == 'yes':
447        builds += ['pdf']
448    if ctx.env.BUILD_SINGLEHTML == 'yes':
449        builds += ['singlehtml']
450
451    for d in building:
[1a9b02e]452        doc = cat.createElement('doc')
453        name = cat.createElement('name')
454        text = cat.createTextNode(d)
455        name.appendChild(text)
456        title = cat.createElement('title')
[782b4fe]457        text = cat.createTextNode(catalogue[d]['title'])
[1a9b02e]458        title.appendChild(text)
459        release = cat.createElement('release')
[782b4fe]460        text = cat.createTextNode(catalogue[d]['release'])
[1a9b02e]461        release.appendChild(text)
462        version = cat.createElement('version')
[782b4fe]463        text = cat.createTextNode(catalogue[d]['version'])
[1a9b02e]464        version.appendChild(text)
465        doc.appendChild(name)
466        doc.appendChild(title)
467        doc.appendChild(release)
468        doc.appendChild(version)
[782b4fe]469        for b in builds:
[1a9b02e]470            output = cat.createElement(b)
[782b4fe]471            text = cat.createTextNode(catalogue[d][b])
[1a9b02e]472            output.appendChild(text)
473            doc.appendChild(output)
474        root.appendChild(doc)
475
[782b4fe]476    catnode = ctx.get_cwd().make_node('catalogue.xml')
[1a9b02e]477    catnode.write(cat.toprettyxml(indent = ' ' * 2, newl = os.linesep))
478
479    cat.unlink()
Note: See TracBrowser for help on using the repository browser.