source: rtems-docs/common/waf.py @ 8e8094a

5
Last change on this file since 8e8094a was 8e8094a, checked in by Chris Johns <chrisj@…>, on 10/13/17 at 00:46:00

build: Add extra source to the sphinx build.

Updates #3177.

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